1. 程式人生 > >Socket網路通訊基礎(第八天)

Socket網路通訊基礎(第八天)

什麼是網路模型

網路程式設計的本質是兩個裝置之間的資料交換,當然,在**計算機網路**中,裝置主要指計算機。資料傳遞本身沒有多大的難度,不就是把一個裝置中的資料傳送給兩外一個裝置,然後接受另外一個裝置反饋的資料。

現在的網路程式設計基本上都是基於請求/響應方式的,也就是一個裝置傳送請求資料給另外一個,然後接收另一個裝置的反饋。

在網路程式設計中,發起連線程式,也就是傳送第一次請求的程式,被稱作客戶端(Client),等待其他程式連線的程式被稱作伺服器(Server)。客戶端程式可以在需要的時候啟動,而伺服器為了能夠時刻相應連線,則需要一直啟動。例如以打電話為例,首先撥號的人類似於客戶端,接聽電話的人必須保持電話暢通類似於伺服器。

連線一旦建立以後,就客戶端和伺服器端就可以進行資料傳遞了,而且兩者的身份是等價的。

在一些程式中,程式既有客戶端功能也有伺服器端功能,最常見的軟體就是BT、emule這類軟體了。

IP地址與域名

在現實生活中,如果要打電話則需要知道對應人的電話號碼,如果要寄信則需要知道收信人的地址。在網路中也是這樣,需要知道一個裝置的位置,則需要使用該裝置的IP地址,具體的連線過程由硬體實現,程式設計師不需要過多的關心。

IP地址是一個規定,現在使用的是IPv4,既由4個0-255之間的數字組成,在計算機內部儲存時只需要4個位元組即可。在計算機中,IP地址是分配給網絡卡的,每個網絡卡有一個唯一的IP地址,如果一個計算機有多個網絡卡,則該臺計算機則擁有多個不同的IP地址,在同一個網路內部,IP地址不能相同。IP地址的概念類似於電話號碼、身份證這樣的概念。

由於IP地址不方便記憶,所以有專門創造了域名(Domain Name)的概念,其實就是給IP取一個字元的名字,例如163.com、sina.com等。IP和域名之間存在一定的對應關係。如果把IP地址類比成身份證號的話,那麼域名就是你的姓名。

其實在網路中只能使用IP地址進行資料傳輸,所以在傳輸以前,需要把域名轉換為IP,這個由稱作DNS的伺服器專門來完成。

IP、域名:所以在網路程式設計中,可以使用IP或域名來標識網路上的一臺裝置。

埠號的概念

網路程式設計就是使用IP地址,或域名,和埠連線到另一臺計算機上對應的程式,按照規定的協議(資料格式)來交換資料,實際程式設計中建立連線和傳送、接收資料在語言級已經實現,做的更多的工作是設計協議,以及編寫生成和解析資料的程式碼罷了,然後把資料轉換成邏輯的結構顯示或控制邏輯即可。

埠:定位到具體應用程式。

Socket入門

什麼是Socket

  1. Socket就是為網路服務提供的一種機制。
  2. 通訊的兩端都有Sokcet。
  3. 網路通訊其實就是Sokcet間的通訊。
  4. 資料在兩個Sokcet間通過IO傳輸。

TCP與UDP在概念上的區別

**udp: **

  • 是面向無連線, 將資料及源的封裝成資料包中,不需要建立連線;
  • 每個資料報的大小在限制64k內;
  • 因無連線,是不可靠協議;
  • 不需要建立連線,速度快。

tcp

  • 建議連線,形成傳輸資料的通道;
  • 在連線中進行大資料量傳輸,以位元組流方式;
  • 通過三次握手完成連線,是可靠協議;
  • 必須建立連線m效率會稍低。

UDP協議

//socket伺服器端
class UdpSocketServer {

	public static void main(String[] args) throws IOException {
		System.out.println("udp伺服器端啟動連線....");
		DatagramSocket ds = new DatagramSocket(8080);
		byte[] bytes = new byte[1024];
		DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
		// 阻塞,等待接受客戶端傳送請求
		ds.receive(dp);
		System.out.println("來源:"+dp.getAddress()+",埠號:"+dp.getPort());
		// 獲取客戶端請求內容
		String str=new String(dp.getData(),0,dp.getLength());
		System.out.println("str:"+str);
		ds.close();
	}

}
// udp客戶端程式碼
public class UdpClient {

	 public static void main(String[] args) throws IOException {
		 System.out.println("udp客戶端啟動連線....");
		 DatagramSocket ds = new DatagramSocket();
		 String str="李四你好";
		 byte[] bytes= str.getBytes();
		 DatagramPacket dp= new DatagramPacket(bytes, bytes.length,InetAddress.getByName("127.0.0.1"),8080);
		 ds.send(dp);
		 ds.close();
	}
	
}

TCP協議

TCP握手協議

在TCP/IP協議中,TCP協議採用三次握手建立一個連線。

  1. 建立連線時,客戶端傳送SYN包(SYN=J)到伺服器,並進入SYN_SEND狀態,等待伺服器確認;
  2. 伺服器收到SYN包,必須確認客戶的SYN(ACK=J+1),同時自己也傳送一個SYN包(SYN=K),即SYN+ACK包,此時伺服器V狀態;
  3. 客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ACK=K+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手。

完成三次握手,客戶端與伺服器開始傳送資料。

TCP分手協議

  1. 客戶端A傳送一個FIN,用來關閉客戶A到伺服器B的資料傳送。
  2. 伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將佔用一個序號。
  3. 伺服器B關閉與客戶端A的連線,傳送一個FIN給客戶端A。
  4. 客戶端A發回ACK報文確認,並將確認序號設定為收到序號加1。
//tcp伺服器端...
class TcpServer {

	public static void main(String[] args) throws IOException {
		System.out.println("socket tcp伺服器端啟動....");
		ServerSocket serverSocket = new ServerSocket(8080);
		// 等待客戶端請求
		Socket accept = serverSocket.accept();
		InputStream inputStream = accept.getInputStream();
		// 轉換成string型別
		byte[] buf = new byte[1024];
		int len = inputStream.read(buf);
		String str = new String(buf, 0, len);
		System.out.println("伺服器接受客戶端內容:" + str);
		serverSocket.close();
	}
}
// TCP客戶端
public class TcpClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("socket tcp 客戶端啟動....");
		Socket socket = new Socket("127.0.0.1", 8080);
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write("我是張三".getBytes());
		socket.close();
	}
}

BIO與NIO

IO(BIO):和NIO區別:其本質就是阻塞和非阻塞的區別

阻塞概念:應用程式在獲取網路資料的時候,如果網路傳輸資料很慢,就會一直等待,直到傳輸完畢為止。

非阻塞概念:應用程式直接可以獲取已經準備就緒好的資料,無需等待。

IO為同步阻塞形式,NIO為同步非阻塞形式,NIO並沒有實現非同步,在JDK1.7後升級NIO庫包,支援非同步非阻塞

同步模型NIO2.0(AIO)

BIO:同步阻塞式IO,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。 NIO:同步非阻塞式IO,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。 AIO(NIO.2):非同步非阻塞式IO,伺服器實現模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動執行緒進行處理。

同步時,應用程式會直接參與IO讀寫操作,並且我們的應用程式會直接阻塞到某一個方法上,直到資料準備就緒;或者採用輪訓的策略實時檢查資料的就緒狀態,如果就緒則獲取資料。

非同步時,則所有的IO讀寫操作交給作業系統,與我們的應用程式沒有直接關係,我們程式不需要關係IO讀寫,當作業系統完成了IO讀寫操作時,會給我們應用程式傳送通知,我們的應用程式直接拿走資料極即可。

筆試題

  1. 為什麼建立連線協議是三次握手,而關閉連線卻是四次握手呢?

    這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在 一個報文裡來發送,但關閉連線時,當收到對方的FIN報文通知時,它僅僅表示對方沒有資料傳送給你了;但未必你所有的資料都全部發送給對方了,所以你可以 未必會馬上會關閉SOCKET,也即你可能還需要傳送一些資料給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連線了,所以它這裡的ACK報 文和FIN報文多數情況下都是分開發送的。

  2. 為什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?

    這是因為雖然雙方都同意關閉連線了,而且握手的4個報文也都協調和傳送完畢,按理可以直接回到CLOSED狀態(就好比從SYN_SEND狀態到ESTABLISH狀態那樣);但是因為我們必須要假想網路是不可靠的,你無法保證你最後傳送的ACK報文會一定被對方收到,因此對方處於LAST_ACK狀態下的SOCKET可能會因為超時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT狀態的作用就是用來重發可能丟失的ACK報文。