Linux(二十四)TCP的三次握手與四次揮手

三次握手

這裏寫圖片描述

服務器在開始的時候,調用socket()函數分配一個文件描述符,然後是填充本地sockaddr_in結構體信息,綁定分配的文件描述符和服務器地址端口,下面就開始建立監聽描述符,調用listen函數,使剛纔的文件描述符成爲一個監聽描述符,
然後就是阻塞等待客戶端連接;
客戶端做的工作就是分配一個文件描述符,填充sockaddr_in信息,然後調用connect函數向服務器發起連接請求,阻塞等待服務器應答

客戶端發起請求,發送SYN同步報文段給服務器;
服務器收到請求後,發送一個ACK應答,同時也發送一個SYN同步報文段;
客戶端收到響應後,發送一個ACK應答,由此雙方通信開始

TCP協議是面向連接的,開始的時候客戶端TCP處於SYN_SENT請求連接狀態,服務器端TCP處於SYN_RCVD等待連接狀態,當客戶端接收到服務器的ACK應答,並且給服務器端發送ACK應答,此時客戶端服務器都變成ESTABLISHED,即連接成功狀態

四次揮手

這裏寫圖片描述

客戶端主動調用close時,就會向服務器發送結束報文段FIN,同時進入FIN_WAIT_1狀態,服務器收到了FIN結束報文段,進入CLOSE_WAIT狀態,處理完之前的數據後,調用close,向客戶端發送FIN,此時服務器處於LAST_ACK狀態,等待最後一個ACK到來,當客戶端收到服務器發送的FIN後,進入TIME_WAIT狀態,然後向服務器發送ACK確認,客戶端要等待2MSL時間纔會進入CLOSED狀態,服務器收到了FIN的ACK就徹底關閉

理解TIME_WAIT狀態

我們先啓動server,然後啓動client,然後用Ctrl-c使server終止,這時馬上運行server,結果是
這裏寫圖片描述
這裏寫圖片描述

這裏的綁定失敗原因就是服務器正處於TIME_WAIT狀態,此端口號已經被佔用,所以不能綁定成功,我們用netstat命令查看一下

這裏寫圖片描述

*TCP協議規定,主動斷開連接的一方要處於TIME_WAIT狀態,等待兩個MSL的時間之後才能回到CLOSED狀態。
*我們用Ctrl+c終止了server,所以server是主動斷開的乙方,在TIME_WAIT期間內不能鑑聽同樣的server端口;
*MSL在RFC1122中規定爲兩分鐘,但是各操作系統的實現不同,在Centos7上默認的是60s;
*可以通過

cat /proc/sys/net/ipv4/tcp_fin_timeout

查看msl的值

這裏寫圖片描述

爲什麼TIME_WAIT的時間是2MSL呢?
*MSL是TCP報文的最大生存時間,因此TIME_WAIT持續存在2MSL的話就可以保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失(否則服務器立刻重啓,可能會受到來自上一個進程的遲到的數據,但是這種數據很可能是有問題的);
*同時也是在理論上保證最後一個報文可靠到達(假設最後一個ACK丟失,那麼服務器再重發一個FIN,這是雖然客戶端的進程不在了,但是TCP連接還在,仍然可以重發LAST_ACK);

如何解決TIME_WAIT狀態引起的bind失敗
如果服務器需要處理非常大量的客戶端連接,這個時候如果由服務器端主動關閉連接,就會產生大量的TIME_WAIT連接,由於我們的請求量很大,就可能導致TIME_WAIT的連接數很多,導致服務器端口不夠用,無法處理新的連接。

於是我們採用的方法就是使用setsockopt()設置socket描述符的選項SO_REUSEADDR爲1,表示允許創建端口號相同但是IP地址不同的多個socket描述符
在server代碼的socket()和bind()調用之間插入下面的代碼

int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章