前言
tcp的關閉不是簡單粗暴的,相對而言是友好優雅的,好聚好散吧。
那麼友好的關閉方式是這樣的:
假設這裡是客戶端請求關閉的,服務端倒過來。
客戶端:我要請求關閉
服務端:我接收到你的請求了,等我把要發的資料發完。
服務端:我要發的資料發完了,可以關閉了。
客戶端:好的,我已經執行清理工作了,關閉結束。
那麼這個時候為什麼服務端直接告訴客戶端可以直接關閉了呢?
為什麼伺服器端要做一些事情呢,到底有啥用。
回到設計的角度上,理論上是越簡單越好的,遇到了什麼問題,才使得我們要增加一個步驟呢。
那麼就開始介紹到底傳送了什麼事情,這要從。。。。
正文
為什麼會有四次揮手呢?
網上有很多故事哈。
下面其中一個故事是:
答:因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都傳送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。
下面自我整理一下。
為什麼要有揮手這個操作呢?
本質上要回到tcp是流的這種操作上。
流有什麼特點呢?就是不知道什麼時候結束。
那麼進行假如客戶端傳送資料完畢後,那麼就需要告訴服務端傳送完畢了。
服務端也需要告訴客戶端收到了客戶端的訊號了,告訴客戶端我已經知道你傳送結束了。這就兩次揮手了。
但是tcp其實是一對一的,雙向通訊的,你客戶端傳送完畢了,服務端不一定資料傳送完畢啊。
那麼客戶端應該還在接收訊息,tcp應該是半關閉了。
那麼服務端傳送資料完畢後,也應該給客戶端一次揮手,告訴客戶端:服務端已經傳送完畢了,這就3次揮手了。
客戶端應該也給服務端傳送訊號,自己收到了服務端結束資料傳輸了,你如果不給服務端傳輸,那麼服務端也不知道你訊號收到了。
那麼其實四次揮手也就是因為tcp是流的這個特性了,因為要告訴對方,流結束了,對方也要回應流結束標誌收到了。
具體實現就是下面這個了:
這裡順便說一下為什麼tcp連線要三次握手呢? 這是因為tcp是雙向通訊了。
A->B 請求連線,那麼說明A->B 是能夠通訊的。
但是B可不一定能到A,因為有防火牆等,那麼B 就要傳送資訊給 A,既是告訴B能收到B的資訊,也是試探一些B是否能傳送給A。
那麼A收到B的資料後,那麼也得回應一下啊,不然B也不知道能傳送資訊到A啊。這就三次握手了。
實驗程式碼服務端:
// See https://aka.ms/new-console-template for more information
using System.Net;
using System.Net.Sockets;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var ipAddress = IPAddress.Parse("127.0.0.1");
EndPoint endPoint = new IPEndPoint(ipAddress, 8888);
socket.Bind(endPoint);
socket.Listen();
var clientSocket = socket.Accept();
while (true)
{
Console.WriteLine("開始接收");
var receiveMessage = new Byte[1000];
var size = clientSocket.Receive(receiveMessage);
if (size == 0)
{
clientSocket.Close();
break;
}
else
{
Console.WriteLine("接收到訊息");
Console.WriteLine("receive message is:"+System.Text.Encoding.UTF8.GetString(receiveMessage));
}
}
實驗程式碼客戶端:
// See https://aka.ms/new-console-template for more information
using System.Net;
using System.Net.Sockets;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var ipAddress = IPAddress.Parse("127.0.0.1");
EndPoint endPoint = new IPEndPoint(ipAddress, 8888);
socket.Connect(endPoint);
socket.Send(System.Text.Encoding.UTF8.GetBytes("hello service"));
Console.WriteLine("傳送成功");
socket.Shutdown(SocketShutdown.Send);
Console.ReadLine();
上面客戶端呼叫Shutdown關閉自己的傳送端。
因為服務端不傳送訊息,直接收到客戶端的傳輸完成訊號後,直接close 雙向關閉就好。
四次揮手的抓包。
結
介紹網路的一些周邊之類的,比如域名、網路地址套接字的一些其他選項。