Java基礎(chǔ):網(wǎng)絡(luò)編程

1. 網(wǎng)絡(luò)編程概述

1.1 計算機網(wǎng)絡(luò)

是指將地理位置不同的具有獨立功能的多臺計算機及其外部設(shè)備,通過通信線路連接起來,在網(wǎng)絡(luò)操作系統(tǒng),網(wǎng)絡(luò)管理軟件及網(wǎng)絡(luò)通信協(xié)議的管理和協(xié)調(diào)下,實現(xiàn)資源共享和信息傳遞的計算機系統(tǒng)。

1.2 網(wǎng)絡(luò)編程

就是用來實現(xiàn)網(wǎng)絡(luò)互連的不同計算機上運行的程序間可以進(jìn)行數(shù)據(jù)交換。

1.3 網(wǎng)絡(luò)模型

計算機網(wǎng)絡(luò)之間以何種規(guī)則進(jìn)行通信,就是網(wǎng)絡(luò)模型研究問題。

網(wǎng)絡(luò)模型一般是指OSI(Open System Interconnection開放系統(tǒng)互連)參考模型或者TCP/IP參考模型。

應(yīng)用層:http、https、ftp,傳輸層:TCP、UDP,網(wǎng)絡(luò)層:IP,物理層,數(shù)據(jù)鏈路層

網(wǎng)絡(luò)模型

1.4 網(wǎng)絡(luò)模型7層概述

  • 物理層

主要定義物理設(shè)備標(biāo)準(zhǔn),如網(wǎng)線的接口類型、光纖的接口類型、各種傳輸介質(zhì)的傳輸速率等。它的主要作用是傳輸比特流(就是由1、0轉(zhuǎn)化為電流強弱來進(jìn)行傳輸,到達(dá)目的地后在轉(zhuǎn)化為1、0,也就是我們常說的數(shù)模轉(zhuǎn)換與模數(shù)轉(zhuǎn)換)。這一層的數(shù)據(jù)叫做比特。

  • 數(shù)據(jù)鏈路層

主要將從物理層接收的數(shù)據(jù)進(jìn)行MAC地址(網(wǎng)卡的地址)的封裝與解封裝。常把這一層的數(shù)據(jù)叫做幀。在這一層工作的設(shè)備是交換機,數(shù)據(jù)通過交換機來傳輸。

  • 網(wǎng)絡(luò)層

主要將從下層接收到的數(shù)據(jù)進(jìn)行IP地址(例192.168.0.1)的封裝與解封裝。在這一層工作的設(shè)備是路由器,常把這一層的數(shù)據(jù)叫做數(shù)據(jù)包。

  • 傳輸層

定義了一些傳輸數(shù)據(jù)的協(xié)議和端口號(WWW端口80等),如:TCP(傳輸控制協(xié)議,傳輸效率低,可靠性強,用于傳輸可靠性要求高,數(shù)據(jù)量大的數(shù)據(jù)),UDP(用戶數(shù)據(jù)報協(xié)議,與TCP特性恰恰相反,用于傳輸可靠性要求不高,數(shù)據(jù)量小的數(shù)據(jù),如QQ聊天數(shù)據(jù)就是通過這種方式傳輸?shù)模?主要是將從下層接收的數(shù)據(jù)進(jìn)行分段和傳輸,到達(dá)目的地址后再進(jìn)行重組。常常把這一層數(shù)據(jù)叫做段。

  • 會話層

通過傳輸層(端口號:傳輸端口與接收端口)建立數(shù)據(jù)傳輸?shù)耐贰V饕谀愕南到y(tǒng)之間發(fā)起會話或者接受會話請求(設(shè)備之間需要互相認(rèn)識可以是IP也可以是MAC或者是主機名)

  • 表示層

主要是進(jìn)行對接收的數(shù)據(jù)進(jìn)行解釋、加密與解密、壓縮與解壓縮等(也就是把計算機能夠識別的東西轉(zhuǎn)換成人能夠能識別的東西(如圖片、聲音等)。

  • 應(yīng)用層

主要是一些終端的應(yīng)用,比如說FTP(各種文件下載),WEB(IE瀏覽),QQ之類的(可以把它理解成我們在電腦屏幕上可以看到的東西.就是終端應(yīng)用)。

PS:

  • 每個網(wǎng)卡的MAC地址都是全球唯一的。
  • 路由器實現(xiàn)將數(shù)據(jù)包發(fā)送到指定的地點。
  • 應(yīng)用軟件之間通信的過程就是層與層之間封包、解封包的過程。
  • OSI參考模型雖然設(shè)計精細(xì),但過于麻煩,效率不高,因此才產(chǎn)生了簡化版的TCP/IP參考模型。

1.5 封包、解封包的過程

封包、解封包

2. 網(wǎng)絡(luò)編程三要素

網(wǎng)絡(luò)模型說完了,我們要進(jìn)行通訊,需要哪些要素呢?

比如說:我要跟你說話
第一個條件:我要先找到你 (IP)
第二個條件:你得有接收數(shù)據(jù)的地方,耳朵 (端口)
第三個條件:我跟你說話,你能接收到,咱按什么方式接收啊,我說英文你懂嗎,說韓文你懂嗎,不懂是吧,所以我還是說中文把(協(xié)議)

2.1 IP地址

網(wǎng)絡(luò)中計算機的唯一標(biāo)識,不易記憶,可用主機名。本地回環(huán)地址:127.0.0.1,主機名:localhost。計算機只能識別二進(jìn)制的數(shù)據(jù),所以我們的IP地址應(yīng)該是一個二進(jìn)制的數(shù)據(jù)。為了方便表示IP地址,我們就把IP地址的每一個字節(jié)上的數(shù)據(jù)換算成十進(jìn)制,然后用.分開來表示:"點分十進(jìn)制"。

所謂IP地址就是給每個連接在Internet上的主機分配的一個32bit地址。按照TCP/IP規(guī)定,IP地址用二進(jìn)制來表示,每個IP地址長32bit,比特?fù)Q算成字節(jié),就是4個字節(jié)。例如一個采用二進(jìn)制形式的IP地址是“00001010000000000000000000000001”,這么長的地址,人們處理起來也太費勁了。為了方便人們的使用,IP地址經(jīng)常被寫成十進(jìn)制的形式,中間使用符號“.”分開不同的字節(jié)。于是,上面的IP地址可以表示為“10.0.0.1”。IP地址的這種表示法叫做“點分十進(jìn)制表示法”,這顯然比1和0容易記憶得多。

通過ping 127.0.0.1可以測試網(wǎng)絡(luò)是不是通,如果不通,可能是網(wǎng)卡出問題了

ip地址

通過ping命令還可以獲取到url對應(yīng)的IP地址,例如獲取網(wǎng)易新聞url(c.m.163.com)的IP地址

ping命令

查看本機IP地址ipconfig

ping命令

IP地址分類

IP地址的組成:IP地址 = 網(wǎng)絡(luò)號碼+主機地址

IPV4數(shù)量已經(jīng)不夠分配,所以產(chǎn)生了IPV6。

ip

InetAddress類的使用

此類表示互聯(lián)網(wǎng)協(xié)議 (IP) 地址

返回值 方法 說明
InetAddress getByName(String host) 根據(jù)主機名或者IP地址的字符串表示得到IP地址對象
String getHostName() 獲取此 IP 地址的主機名
String getHostAddress() 返回 IP 地址字符串

代碼示例:

package cn.itcast_01;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
        // public static InetAddress getByName(String host)
        // InetAddress address = InetAddress.getByName("liuyi");
        // InetAddress address = InetAddress.getByName("192.168.12.92");
        InetAddress address = InetAddress.getByName("192.168.12.63");

        // 獲取兩個東西:主機名,IP地址
        // public String getHostName()
        String name = address.getHostName();
        // public String getHostAddress()
        String ip = address.getHostAddress();
        System.out.println(name + "---" + ip);
    }
}

運行結(jié)果:

ip

2.2 端口號

正在運行的程序的標(biāo)識,用于標(biāo)識進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識。有效端口:065535,其中01024系統(tǒng)使用或保留端口。

端口分為:物理端口,網(wǎng)卡口;邏輯端口,我們指的就是邏輯端口。

  • A:每個網(wǎng)絡(luò)程序都會至少有一個邏輯端口
  • B:用于標(biāo)識進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識
  • C:有效端口:065535,其中01024系統(tǒng)使用或保留端口。
  • D:所謂防火墻,其功能就是將發(fā)送到某程序端口的數(shù)據(jù)屏蔽掉以及將從該程序端口發(fā)出的數(shù)據(jù)也屏蔽掉。
端口號

2.3 傳輸協(xié)議

傳輸協(xié)議就是通訊的規(guī)則,常見協(xié)議:TCP,UDP。

UDP將數(shù)據(jù)源和目的封裝成數(shù)據(jù)包中,不需要建立連接;每個數(shù)據(jù)報的大小在限制在64k;因無連接,是不可靠協(xié)議;不需要建立連接,速度快

TCP建立連接,形成傳輸數(shù)據(jù)的通道;在連接中進(jìn)行大數(shù)據(jù)量傳輸;通過三次握手完成連接,是可靠協(xié)議;必須建立連接,效率會稍低

UDP和TCP的特點

  • UDP:面向無連接;不可靠;速度快;將數(shù)據(jù)封包傳輸,數(shù)據(jù)包最大64k
    舉例:聊天留言,在線視頻,視頻會議,發(fā)短信,郵局包裹。
  • TCP:面向連接;安全可靠效率稍低;通過三次握手確保連接的建立。
    舉例:下載,打電話,QQ聊天(你在線嗎,在線,就回應(yīng)下,就開始聊天了)

2.4 域名解析

域名解析

在瀏覽器中輸入新浪的域名,DNS解析域名成IP,然后計算機再通過獲取到的IP訪問新浪服務(wù)器。

域名解析,最先走是本地的hosts(C:\WINDOWS\system32\drivers\etc\hosts)文件,解析失敗了,才去訪問DNS服務(wù)器解析、獲取IP地址。

域名解析
域名解析
import java.net.InetAddress;
import java.net.UnknownHostException;

public class IPDemo
{
    public static void main(String[] args) throws UnknownHostException {

        InetAddress ip = InetAddress.getLocalHost();

        ip = InetAddress.getByName("192.168.1.110");

        System.out.println(ip.getHostAddress());
        System.out.println(ip.getHostName());
    }
}

運行結(jié)果

域名解析

應(yīng)用:通過hosts文件可以屏蔽游戲網(wǎng)站內(nèi)容彈出,例如:在hosts文件中添加,127.0.0.1 www.game18.com

3. Socket套接字

3.1 Socket套接字

網(wǎng)絡(luò)上具有唯一標(biāo)識的IP地址和端口號組合在一起才能構(gòu)成唯一能識別的標(biāo)識符套接字。

3.2 Socket原理機制

  • 通信的兩端都有Socket
  • 網(wǎng)絡(luò)通信其實就是Socket間的通信
  • 數(shù)據(jù)在兩個Socket間通過IO傳輸

3.3 Socket機制圖解

socket

4. UDP編程

UDP:UDP 協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議,在網(wǎng)絡(luò)中它與TCP 協(xié)議一樣用于處理數(shù)據(jù)包,是一種無連接的協(xié)議。在OSI 模型中,在第四層——傳輸層,處于IP 協(xié)議的上一層。UDP 有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進(jìn)行排序的缺點,也就是說,當(dāng)報文發(fā)送之后,是無法得知其是否安全完整到達(dá)的。UDP 用來支持那些需要在計算機之間傳輸數(shù)據(jù)的網(wǎng)絡(luò)應(yīng)用。包括網(wǎng)絡(luò)視頻會議系統(tǒng)在內(nèi)的眾多的客戶/服務(wù)器模式的網(wǎng)絡(luò)應(yīng)用都需要使用UDP協(xié)議。UDP 協(xié)議從問世至今已經(jīng)被使用了很多年,雖然其最初的光彩已經(jīng)被一些類似協(xié)議所掩蓋,但是即使是在今天UDP 仍然不失為一項非常實用和可行的網(wǎng)絡(luò)傳輸層協(xié)議。

4.1 UDP傳輸

  • DatagramSocket與DatagramPacket
  • 建立發(fā)送端,接收端
  • 建立數(shù)據(jù)包
  • 調(diào)用Socket的發(fā)送接收方法
  • 關(guān)閉Socket
  • 發(fā)送端與接收端是兩個獨立的運行程序

4.2 DatagramSocket

此類表示用來發(fā)送和接收數(shù)據(jù)報包的套接字

數(shù)據(jù)報套接字是包投遞服務(wù)的發(fā)送或接收點。每個在數(shù)據(jù)報套接字上發(fā)送或接收的包都是單獨編址和路由的。從一臺機器發(fā)送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序到達(dá)

在 DatagramSocket 上總是啟用 UDP 廣播發(fā)送。為了接收廣播包,應(yīng)該將 DatagramSocket 綁定到通配符地址。在某些實現(xiàn)中,將 DatagramSocket 綁定到一個更加具體的地址時廣播包也可以被接收。

構(gòu)造方法

DatagramSocket(int port) // 創(chuàng)建數(shù)據(jù)報套接字并將其綁定到本地主機上的指定端口
DatagramSocket(int port, InetAddress laddr) // 創(chuàng)建數(shù)據(jù)報套接字,將其綁定到指定的本地地址

UDP傳輸-發(fā)送端思路

  • 建立udp的socket服務(wù)
  • 將要發(fā)送的數(shù)據(jù)封裝成數(shù)據(jù)包
  • 通過udp的socket服務(wù),將數(shù)據(jù)包發(fā)送出
  • 關(guān)閉資源
package cn.itcast_02;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
 * UDP協(xié)議發(fā)送數(shù)據(jù):
 * A:創(chuàng)建發(fā)送端Socket對象
 * B:創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
 * C:調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
 * D:釋放資源
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端Socket對象
        // DatagramSocket()
        DatagramSocket ds = new DatagramSocket();

        // 創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
        // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        // 創(chuàng)建數(shù)據(jù)
        byte[] bys = "hello,udp,我來了".getBytes();
        // 長度
        int length = bys.length;
        // IP地址對象
        InetAddress address = InetAddress.getByName("192.168.12.92");
        // 端口
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);

        // 調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
        // public void send(DatagramPacket p)
        ds.send(dp);

        // 釋放資源
        ds.close();
    }
}

4.3 DatagramPacket

此類表示數(shù)據(jù)報包。數(shù)據(jù)報包用來實現(xiàn)無連接包投遞服務(wù)。每條報文僅根據(jù)該包中包含的信息從一臺機器路由到另一臺機器。從一臺機器發(fā)送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序到達(dá)。不對包投遞做出保證。

構(gòu)造方法

  • DatagramPacket(byte[] buf, int length)
    構(gòu)造 DatagramPacket,用來接收長度為 length 的數(shù)據(jù)包。

  • DatagramPacket(byte[] buf, int length, InetAddress address, int port)
    構(gòu)造數(shù)據(jù)報包,用來將長度為 length 的包發(fā)送到指定主機上的指定端口號。

  • DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
    構(gòu)造數(shù)據(jù)報包,用來將長度為 length 偏移量為 offset 的包發(fā)送到指定主機上的指定端口號。

UDP傳輸-接收端思路

  • 建立udp的socket服務(wù).
  • 通過receive方法接收數(shù)據(jù)
  • 將收到的數(shù)據(jù)存儲到數(shù)據(jù)包對象中
  • 通過數(shù)據(jù)包對象的功能來完成對接收到數(shù)據(jù)進(jìn)行解析
  • 可以對資源進(jìn)行關(guān)閉
package cn.itcast_02;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 * UDP協(xié)議接收數(shù)據(jù):
 * A:創(chuàng)建接收端Socket對象
 * B:創(chuàng)建一個數(shù)據(jù)包(接收容器)
 * C:調(diào)用Socket對象的接收方法接收數(shù)據(jù)
 * D:解析數(shù)據(jù)包,并顯示在控制臺
 * E:釋放資源
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端Socket對象
        // DatagramSocket(int port)
        DatagramSocket ds = new DatagramSocket(10086);

        // 創(chuàng)建一個數(shù)據(jù)包(接收容器)
        // DatagramPacket(byte[] buf, int length)
        byte[] bys = new byte[1024];
        int length = bys.length;
        DatagramPacket dp = new DatagramPacket(bys, length);

        // 調(diào)用Socket對象的接收方法接收數(shù)據(jù)
        // public void receive(DatagramPacket p)
        ds.receive(dp); // 阻塞式

        // 解析數(shù)據(jù)包,并顯示在控制臺
        // 獲取對方的ip
        // public InetAddress getAddress()
        InetAddress address = dp.getAddress();
        String ip = address.getHostAddress();
        // public byte[] getData():獲取數(shù)據(jù)緩沖區(qū)
        // public int getLength():獲取數(shù)據(jù)的實際長度
        byte[] bys2 = dp.getData();
        int len = dp.getLength();
        String s = new String(bys2, 0, len);
        System.out.println(ip + "傳遞的數(shù)據(jù)是:" + s);

        // 釋放資源
        ds.close();
    }
}

運行結(jié)果:

udp

4.4 UDP案例

從鍵盤錄入數(shù)據(jù)進(jìn)行發(fā)送,如果輸入的是886那么客戶端就結(jié)束輸入數(shù)據(jù)。

發(fā)送端

package cn.itcast_04;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 * 數(shù)據(jù)來自于鍵盤錄入
 * 鍵盤錄入數(shù)據(jù)要自己控制錄入結(jié)束。
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端的Socket對象
        DatagramSocket ds = new DatagramSocket();

        // 封裝鍵盤錄入數(shù)據(jù)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = null;
        while ((line = br.readLine()) != null) {
            if ("886".equals(line)) {
                break;
            }

            // 創(chuàng)建數(shù)據(jù)并打包
            byte[] bys = line.getBytes();
            // DatagramPacket dp = new DatagramPacket(bys, bys.length,
            // InetAddress.getByName("192.168.12.92"), 12345);
            DatagramPacket dp = new DatagramPacket(bys, bys.length,
                    InetAddress.getByName("192.168.12.255"), 12345);

            // 發(fā)送數(shù)據(jù)
            ds.send(dp);
        }

        // 釋放資源
        ds.close();
    }
}

運行結(jié)果:

udp

接收端

package cn.itcast_04;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
 * 多次啟動接收端:
 *      java.net.BindException: Address already in use: Cannot bind
 *      端口被占用。
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端的Socket對象
        DatagramSocket ds = new DatagramSocket(12345);

        while (true) {
            // 創(chuàng)建一個包裹
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);

            // 接收數(shù)據(jù)
            ds.receive(dp);

            // 解析數(shù)據(jù)
            String ip = dp.getAddress().getHostAddress();
            String s = new String(dp.getData(), 0, dp.getLength());
            System.out.println("from " + ip + " data is : " + s);
        }

        // 釋放資源
        // 接收端應(yīng)該一直開著等待接收數(shù)據(jù),是不需要關(guān)閉
        // ds.close();
    }
}

運行結(jié)果:

udp

5. TCP編程

TCP/IP:Transmission Control Protocol/Internet Protocol 的簡寫,中譯名為傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議,又名網(wǎng)絡(luò)通訊協(xié)議,是Internet 最基本的協(xié)議、Internet 國際互聯(lián)網(wǎng)絡(luò)的基礎(chǔ),由網(wǎng)絡(luò)層的IP 協(xié)議和傳輸層的TCP協(xié)議組成。TCP/IP 定義了電子設(shè)備如何連入因特網(wǎng),以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。協(xié)議采用了4 層的層級結(jié)構(gòu),每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求。通俗而言:TCP 負(fù)責(zé)發(fā)現(xiàn)傳輸?shù)膯栴},一有問題就發(fā)出信號,要求重新傳輸,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡亍6鳬P 是給因特網(wǎng)的每一臺聯(lián)網(wǎng)設(shè)備規(guī)定一個地址。

TCP/IP 協(xié)議棧主要分為四層:應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層,每層都有相應(yīng)的協(xié)議,如下圖:

tcp

所謂的協(xié)議就是雙方進(jìn)行數(shù)據(jù)傳輸?shù)囊环N格式。

5.1 TCP傳輸

  • Socket和ServerSocket
  • 建立客戶端和服務(wù)器端
  • 建立連接后,通過Socket中的IO流進(jìn)行數(shù)據(jù)的傳輸
  • 關(guān)閉socket
  • 同樣,客戶端與服務(wù)器端是兩個獨立的應(yīng)用程序。

5.2 Socket

此類實現(xiàn)客戶端套接字(也可以就叫“套接字”)。套接字是兩臺機器間通信的端點。

構(gòu)造方法

  • Socket(String host, int port) :創(chuàng)建一個流套接字并將其連接到指定主機上的指定端口號。
  • Socket(InetAddress address, int port) :創(chuàng)建一個流套接字并將其連接到指定 IP 地址的指定端口號。

TCP傳輸-客戶端思路

  • 建立客戶端的Socket服務(wù),并明確要連接的服務(wù)器。
  • 如果連接建立成功,就表明,已經(jīng)建立了數(shù)據(jù)傳輸?shù)耐ǖ?就可以在該通道通過IO進(jìn)行數(shù)據(jù)的讀取和寫入.該通道稱為Socket流,Socket流中既有讀取流,也有寫入流.
  • 通過Socket對象的方法,可以獲取這兩個流
  • 通過流的對象可以對數(shù)據(jù)進(jìn)行傳輸
  • 如果傳輸數(shù)據(jù)完畢,關(guān)閉資源
package cn.itcast_06;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/*
 * TCP協(xié)議發(fā)送數(shù)據(jù):
 * A:創(chuàng)建發(fā)送端的Socket對象
 *      這一步如果成功,就說明連接已經(jīng)建立成功了。
 * B:獲取輸出流,寫數(shù)據(jù)
 * C:釋放資源
 *
 * 連接被拒絕。TCP協(xié)議一定要先看服務(wù)器。
 * java.net.ConnectException: Connection refused: connect
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端的Socket對象
        // Socket(InetAddress address, int port)
        // Socket(String host, int port)
        // Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);
        Socket s = new Socket("192.168.12.92", 8888);

        // 獲取輸出流,寫數(shù)據(jù)
        // public OutputStream getOutputStream()
        OutputStream os = s.getOutputStream();
        os.write("hello,tcp,我來了".getBytes());

        // 釋放資源
        s.close();
    }
}

5.3 ServerSocket

此類實現(xiàn)服務(wù)器套接字。服務(wù)器套接字等待請求通過網(wǎng)絡(luò)傳入。它基于該請求執(zhí)行某些操作,然后可能向請求者返回結(jié)果。

構(gòu)造方法

ServerSocket(int port) // 創(chuàng)建綁定到特定端口的服務(wù)器套接字

TCP傳輸-服務(wù)器端思路

  • 建立服務(wù)器端的socket服務(wù),需要一個端口
  • 服務(wù)端沒有直接流的操作,而是通過accept方法獲取客戶端對象,在通過獲取到的客戶端對象的流和客戶端進(jìn)行通信
  • 通過客戶端的獲取流對象的方法,讀取數(shù)據(jù)或者寫入數(shù)據(jù)
  • 如果服務(wù)完成,需要關(guān)閉客戶端,然后關(guān)閉服務(wù)器,但是,一般會關(guān)閉客戶端,不會關(guān)閉服務(wù)器,因為服務(wù)端是一直提供服務(wù)的
package cn.itcast_06;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
 * TCP協(xié)議接收數(shù)據(jù):
 * A:創(chuàng)建接收端的Socket對象
 * B:監(jiān)聽客戶端連接。返回一個對應(yīng)的Socket對象
 * C:獲取輸入流,讀取數(shù)據(jù)顯示在控制臺
 * D:釋放資源
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端的Socket對象
        // ServerSocket(int port)
        ServerSocket ss = new ServerSocket(8888);

        // 監(jiān)聽客戶端連接。返回一個對應(yīng)的Socket對象
        // public Socket accept()
        Socket s = ss.accept(); // 偵聽并接受到此套接字的連接。此方法在連接傳入之前一直阻塞。

        // 獲取輸入流,讀取數(shù)據(jù)顯示在控制臺
        InputStream is = s.getInputStream();

        byte[] bys = new byte[1024];
        int len = is.read(bys); // 阻塞式方法
        String str = new String(bys, 0, len);

        String ip = s.getInetAddress().getHostAddress();

        System.out.println(ip + "---" + str);

        // 釋放資源
        s.close();
        // ss.close(); //這個不應(yīng)該關(guān)閉
    }
}

5.4 TCP傳輸案例

客戶端鍵盤錄入,服務(wù)器輸出到控制臺

客戶端:

package cn.itcast_08;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/*
 * 客戶端鍵盤錄入,服務(wù)器輸出到控制臺
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 22222);

        // 鍵盤錄入數(shù)據(jù)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 把通道內(nèi)的流給包裝一下
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) {
            // 鍵盤錄入數(shù)據(jù)要自定義結(jié)束標(biāo)記
            if ("886".equals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        // 釋放資源
        // bw.close();
        // br.close();
        s.close();
    }
}

運行結(jié)果:

tcp

服務(wù)器端:

package cn.itcast_08;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(22222);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 包裝通道內(nèi)容的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        // br.close();
        s.close();
        // ss.close();
    }
}

運行結(jié)果:

tcp

5.5 上傳圖片案例

客戶端:

package cn.itcast_13;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 19191);

        // 封裝圖片文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                "林青霞.jpg"));
        // 封裝通道內(nèi)的流
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }

        s.shutdownOutput();

        // 讀取反饋
        InputStream is = s.getInputStream();
        byte[] bys2 = new byte[1024];
        int len2 = is.read(bys2);
        String client = new String(bys2, 0, len2);
        System.out.println(client);

        // 釋放資源
        bis.close();
        s.close();
    }
}

服務(wù)器端:

package cn.itcast_13;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(19191);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 封裝通道內(nèi)流
        BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
        // 封裝圖片文件
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("mn.jpg"));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }

        // 給一個反饋
        OutputStream os = s.getOutputStream();
        os.write("圖片上傳成功".getBytes());

        bos.close();
        s.close();
    }
}

運行結(jié)果:

tcp

5.6 TCP傳輸容易出現(xiàn)的問題

  • 客戶端連接上服務(wù)端,兩端都在等待,沒有任何數(shù)據(jù)傳輸
  • 通過例程分析:因為read方法或者readLine方法是阻塞式
  • 解決辦法:自定義結(jié)束標(biāo)記,使用shutdownInput,shutdownOutput方法

6. TCP、UDP 特點對比

TCP 協(xié)議是面向連接、保證高可靠性(數(shù)據(jù)無丟失、數(shù)據(jù)無失序、數(shù)據(jù)無錯誤、數(shù)據(jù)無重復(fù)到達(dá))傳輸層協(xié)議。UDP 協(xié)議也是傳輸層協(xié)議,它是無連接,不保證可靠的傳輸層協(xié)議。

TCP UDP
面向連接 面向非連接
可靠的連接 不可靠的連接
速度慢 速度快
大文件、重要的數(shù)據(jù)等 適合小數(shù)據(jù)、不重要

7. TCP 三次握手過程

1、請求端(通常稱為客戶)發(fā)送一個SYN 段指明客戶打算連接的服務(wù)器的端口,以及初始序號(ISN)

2、服務(wù)器發(fā)回包含服務(wù)器的初始序號的SYN 報文段(報文段2)作為應(yīng)答。同時,將確認(rèn)序號設(shè)置為客戶的ISN加1 以對客戶的SYN 報文段進(jìn)行確認(rèn)。

3、客戶必須將確認(rèn)序號設(shè)置為服務(wù)器的ISN 加1 以對服務(wù)器的SYN 報文段進(jìn)行確認(rèn)(報文段3)這三個報文段完成連接的建立。這個過程也稱為三次握手(three-way handshake)。

上面的過程如下圖所示:

tcp

8. 客戶端和服務(wù)器端原理

8.1 常見的客戶端、服務(wù)器端

最常見的客戶端:瀏覽器,IE/chrome
最常見的服務(wù)端:服務(wù)器,Tomcat

8.2 常見網(wǎng)絡(luò)結(jié)構(gòu)

網(wǎng)絡(luò)結(jié)構(gòu)

8.3 URL&URI

URI:統(tǒng)一資源標(biāo)識符

URI是統(tǒng)一資源標(biāo)識符,是一個用于標(biāo)識某一互聯(lián)網(wǎng)資源名稱的字符串。 該種標(biāo)識允許用戶對任何(包括本地和互聯(lián)網(wǎng))的資源通過特定的協(xié)議進(jìn)行交互操作。URI由包括確定語法和相關(guān)協(xié)議的方案所定義。由是三個組成部分:訪問資源的命名機制、存放資源的主機名、資源自身的名稱,由路徑表示。

URL:統(tǒng)一資源定位符

也就是說根據(jù)URL能夠定位到網(wǎng)絡(luò)上的某個資源,它是指向互聯(lián)網(wǎng)“資源”的指針。

每個URL都是URI,但不一定每個URI都是URL。這是因為URI還包括一個子類,即統(tǒng)一資源名稱(URN),它命名資源但不指定如何定位資源。

URL是統(tǒng)一資源定位,是對可以從互聯(lián)網(wǎng)上得到的資源的位置和訪問方法的一種簡潔的表示,是互聯(lián)網(wǎng)上標(biāo)準(zhǔn)資源的地址。互聯(lián)網(wǎng)上的每個文件都有一個唯一的URL,它包含的信息指出文件的位置以及瀏覽器應(yīng)該怎么處理它。

比如百度URL即是http://www.baidu.com

9. TCP的三次握手/四次揮手

TCP是面向連接的運輸層協(xié)議,TCP協(xié)議提供可靠的連接服務(wù),所以用了建立鏈接的三次握手和關(guān)閉連接的四次揮手來保證可靠服務(wù)。
通過TCP通信就像是兩個應(yīng)用在打電話一樣,打電話前得先撥號建立連接,通話結(jié)束后要掛機釋放連接。

9.1 建立TCP連接的三次握手

TCP連接的三次握手分別為:

  • 客戶端發(fā)送一個帶SYN標(biāo)志的TCP報文到服務(wù)器,表示告訴服務(wù)器我想建立一個連接。
  • 服務(wù)器收到客戶端的帶SYN標(biāo)志的文后,就給客戶端回復(fù)一個帶ACK標(biāo)志和帶SYN標(biāo)志的報文,ACK表示回復(fù)客戶端:OK,我準(zhǔn)備好了建立連接;然后SYN表示服務(wù)器又問客戶端:你準(zhǔn)備好建立連接了么?
  • 然后客戶端又要發(fā)送一個帶ACK標(biāo)志的TCP報文,回答服務(wù)器說:我準(zhǔn)備好了。
    然后一個TCP連接就建立起來了。

SYN相當(dāng)于詢問的標(biāo)志,ACK相當(dāng)于回復(fù)的標(biāo)志。

這里有一個問題:為什么最后客戶端還要發(fā)送一次確認(rèn)呢?這主要是防止已經(jīng)失效了的請求報文段突然又傳到了服務(wù)器,因而產(chǎn)生錯誤。
“已經(jīng)失效了的請求報文段”大致是這樣產(chǎn)生的:A發(fā)出第一個連接請求報文段并沒有丟失,在一些網(wǎng)絡(luò)結(jié)點上面長時間滯留,以致延誤到連接釋放以后的某個時間才到達(dá)B。本來這是一個早已失效的報文段。但B收到這個失效的報文段后,就誤以為是A發(fā)出的又一次新的連接請求,于是就向A發(fā)出確認(rèn)報文段,同意建立連接,如果不采用三次握手,那么只要B發(fā)出確認(rèn)后,新的連接就建立了。

9.2 釋放TCP連接的四次揮手

由于TCP是全雙工的,所以在釋放TCP連接時,要雙方都得單獨關(guān)閉。意思就是服務(wù)器和客戶端都要釋放連接。原則是某一方主動關(guān)閉時,先發(fā)一個FIN報文來表示終止這個方向的連接,收到一個FIN報文就意味著這個方向不再有數(shù)據(jù)流動,但另一個方向仍可以有數(shù)據(jù)流動,當(dāng)這一個方向也發(fā)送了FIN報文后,那么這一方的連接也可以關(guān)閉了。
釋放TCP連接相對于要復(fù)雜點,具體釋放TCP連接的四次揮手流程如下:

  • A發(fā)送一個FIN給B,說:我這邊要傳給你的數(shù)據(jù)已經(jīng)傳完了,我要關(guān)閉連接了。A進(jìn)入FIN-WAIT-1狀態(tài),等待B確認(rèn)。
  • B收到了上面的FIN報文后,回復(fù)一個ACK報文說:OK。A就關(guān)閉了A->B的連接。但是此時B還能給A發(fā)送數(shù)據(jù),A也能接收B發(fā)來的數(shù)據(jù)。(此時A收到確認(rèn)后進(jìn)入FIN-WAIT-2狀態(tài)。TCP處于半關(guān)閉狀態(tài))
  • 當(dāng)B也發(fā)送完數(shù)據(jù)后,就給A發(fā)送一個FIN報文說:我這邊要傳給你的數(shù)據(jù)也已經(jīng)傳完了,我也要關(guān)閉連接了。(B進(jìn)入LAST-ACK狀態(tài),等待A確認(rèn))
  • A收到了上面的報文后,回復(fù)一個ACK報文說:OK。A進(jìn)入TIME-WAIT狀態(tài)。現(xiàn)在TCP連接還沒有釋放掉,然后經(jīng)過等待計時器(TIME-WAIT timer)設(shè)置的時間2MSL后,A才進(jìn)入CLOSE狀態(tài)。

然后,當(dāng)A撤銷相應(yīng)的傳輸控制塊TCB后,一個TCP連接就關(guān)閉了。

10. Http、Tcp、Udp、Socket的區(qū)別

IP,網(wǎng)絡(luò)層協(xié)議;TCP和UDP,傳輸層協(xié)議;HTTP,應(yīng)用層協(xié)議;SOCKET:TCP/IP網(wǎng)絡(luò)的API。

TCP/IP代表傳輸控制協(xié)議/網(wǎng)際協(xié)議,指的是一系列協(xié)議。

TCP和UDP使用IP協(xié)議從一個網(wǎng)絡(luò)傳送數(shù)據(jù)包到另一個網(wǎng)絡(luò)。把IP想像成一種高速公路,它允許其它協(xié)議在上面行駛并找到到其它電腦的出口。TCP和UDP是高速公路上的“卡車”,它們攜帶的貨物就是像HTTP,文件傳輸協(xié)議FTP這樣的協(xié)議等。

TCP和UDP是FTP,HTTP和SMTP之類使用的傳輸層協(xié)議。雖然TCP和UDP都是用來傳輸其他協(xié)議的,它們卻有一個顯著的不同:TCP提供有保證的數(shù)據(jù)傳輸,而UDP不提供。這意味著TCP有一個特殊的機制來確保數(shù)據(jù)安全的不出錯的從一個端點傳到另一個端點,而UDP不提供任何這樣的保證。

HTTP(超文本傳輸協(xié)議)是利用TCP在兩臺電腦(通常是Web服務(wù)器和客戶端)之間傳輸信息的協(xié)議。客戶端使用Web瀏覽器發(fā)起HTTP請求給Web服務(wù)器,Web服務(wù)器發(fā)送被請求的信息給客戶端。

記住,需要IP協(xié)議來連接網(wǎng)絡(luò);TCP是一種允許我們安全傳輸數(shù)據(jù)的機制,使用TCP協(xié)議來傳輸數(shù)據(jù)的HTTP是Web服務(wù)器和客戶端使用的特殊協(xié)議。

Socket 接口是TCP/IP網(wǎng)絡(luò)的API,Socket接口定義了許多函數(shù)或例程,用以開發(fā)TCP/IP網(wǎng)絡(luò)上的應(yīng)用程序。

本節(jié)原文鏈接:http://www.lxweimin.com/p/1f512687ea19

11. URL

URI:統(tǒng)一資源標(biāo)示符。

URL:統(tǒng)一資源定位符,也就是說根據(jù)URL能夠定位到網(wǎng)絡(luò)上的某個資源,它是指向互聯(lián)網(wǎng)“資源”的指針。

每個URL都是URI,但不一定每個URI都是URL。這是因為URI還包括一個子類,即統(tǒng)一資源名稱(URN),它命名資源但不指定如何定位資源。

public class URLDemo
{
    public static void main(String[] args) throws MalformedURLException,IOException {
        String str_url = "http://192.168.1.100:8080/myweb/1.html?name=lisi";
        URL url = new URL(str_url);
        System.out.println("getProtocol:" + url.getProtocol());
        System.out.println("getHost:" + url.getHost());
        System.out.println("getPort:" + url.getPort());
        System.out.println("getFile:" + url.getFile());
        System.out.println("getPath:" + url.getPath());
        System.out.println("getQuery:" + url.getQuery());
        InputStream in = url.openStream();//相當(dāng)于 url.openConnection().getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(text);
        in.close();
    }
}

運行結(jié)果

url

之所以運行結(jié)果中響應(yīng)頭不見了,只能看到主體數(shù)據(jù)的原因在于:URLConnection對象已經(jīng)把響應(yīng)頭
給解析了

12. URLConnection

public class URLDemo
{
    public static void main(String[] args) throws MalformedURLException,IOException {
        String str_url = "http://192.168.1.100:8080/myweb/1.html?name=lisi";
        URL url = new URL(str_url);
        //獲取url對象的Url連接器對象。將連接封裝成了對象:
        // java中內(nèi)置的可以解析的具體協(xié)議對象+socket。
        URLConnection conn = url.openConnection();
        System.out.println(conn);
        //由于URLConnection對象已經(jīng)把響應(yīng)頭給解析了,所以,
        // 可以通過URLConnection對象獲取響應(yīng)頭某屬性名對應(yīng)的屬性值。
        String value = conn.getHeaderField("Content-Type");
        System.out.println(value);
    }
}

運行結(jié)果

urlconnection

13. HttpURLConnection

URL newURL = new URL(url);
URLConnection urlConnection = newURL.openConnection();
urlConnection.setConnectTimeout(mConfig.connTimeOut);
urlConnection.setReadTimeout(mConfig.soTimeOut);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);

// HttpsURLConnection
HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
HttpsURLConnection.setDefaultHostnameVerifier();

HttpURLConnection常用方法

方法聲明 功能描述
addRequestProperty() 添加請求屬性
setRequestMethod() 設(shè)置請求方式
connect() 連接網(wǎng)絡(luò)
disconnect() 斷開連接
setDoOutput() 設(shè)置打開連接對象輸出流,把要提交的數(shù)據(jù)寫入流中
setDoInput() 設(shè)置打開連接對象輸入流
setConnectTimeout() 設(shè)置連接超時
setReadTimeout() 設(shè)置讀取超時
setUseCaches() 設(shè)置是否使用緩存
getResponseCode() 獲取響應(yīng)碼
getOutputStream() 獲取輸出流
getInputStream() 獲取輸入流
getErrorStream() 獲取錯誤流
getResponseMessage() 獲取響應(yīng)信息
getContentLength() 獲取內(nèi)容長度
getContentEncoding() 獲取內(nèi)容編碼
getContentType() 獲取內(nèi)容類型
getHeaderFields() 獲取所有的頭字段

setRequestProperty和addRequestProperty的區(qū)別

setRequestProperty和addRequestProperty的區(qū)別就是,setRequestProperty會覆蓋已經(jīng)存在的key的所有values,有清零重新賦值的作用。而addRequestProperty則是在原來key的基礎(chǔ)上繼續(xù)添加其他value。

/**
 * Adds the given property to the request header. Existing properties with
 * the same name will not be overwritten by this method.
 */
 public void addRequestProperty(String field, String newValue) {
        ...
    }

字節(jié)流轉(zhuǎn)換為字符

public class Tools {
    public static String getTextFromStream(InputStream is) {
        try {
            byte[] b = new byte[1024];
            int len;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            while ((len = is.read(b)) != -1) {
                bos.write(b, 0, len);
            }
            //把輸出流里的內(nèi)容轉(zhuǎn)換成字節(jié)數(shù)組
            String text = new String(bos.toByteArray());
            return text;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

14. URLEncoder和URLDecoder

URLEncoder.encode();
URLDecoder.decode();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 個人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,083評論 0 8
  • 1.這篇文章不是本人原創(chuàng)的,只是個人為了對這部分知識做一個整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,110評論 6 174
  • 短篇集。三個短篇,前兩個不錯!很有創(chuàng)意!總的來說還是有點太陰暗、負(fù)面了! 內(nèi)容簡介:(摘自網(wǎng)絡(luò)) 《只有你聽到Ca...
    烏麗曼閱讀 1,205評論 0 1
  • 每個人都希望他們有更好的面試問題。因此,我咨詢了各個領(lǐng)域中的創(chuàng)始人或CEO,問了他們最喜愛的面試問題是什么以及這些...
    小好閱讀 854評論 0 4
  • 生活在 Linux 下,折騰肯定是少不了的,所以玩壞的情況也是常有的事情。如果有一臺時光機器可以讓系統(tǒng)回到某個時間...
    左藍(lán)閱讀 6,429評論 0 9