計算機網路再次整理————tcp的關閉[七]

敖毛毛發表於2022-02-07

前言

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 雙向關閉就好。

四次揮手的抓包。

介紹網路的一些周邊之類的,比如域名、網路地址套接字的一些其他選項。

相關文章