10深刻理解TCP/IP的11種協議狀態

基本網絡模型

在這裏插入圖片描述

TCP狀態轉換圖

在這裏插入圖片描述

TCP/IP的11種狀態

在這裏插入圖片描述

第11種狀態

在這裏插入圖片描述

1、注意幾個要點:

  • 任何一端都可以主動關閉連接。
  • 在調用accept()函數之前,兩端已經進入了ESTABLISHED狀態。
在調用listen(int socked, int backlog)函數的時候,做了2件事情:
(1)將主動套接字轉換爲被動套接字。TCP狀態轉換圖從CLOSED狀態轉換爲LISTEN狀態
(2)backlog規定了內核應該爲相應的套接字排隊的最大連接個數。內核爲這個監聽套接字維護兩個隊列:(兩個隊列之和不能超過backlog)

a:未完成連接隊列:此時這個隊列中的套接字處於SYN_RECV狀態。

b:已完成連接隊列:此時這個隊列中的套接字處於ESTABLISHED狀態。

	也就是說在listen()的時候,套接字有2種狀態SYN_RECV 和 ESTABLISHED。
	accept()是從已完成隊列中的隊頭中獲取已經處於ESTABLISHED狀態的套接字,然後accept()返回給用戶進程,若隊列爲空,則accept()投入睡眠,直到已完成隊列中有新的一項。
  • socket()->bind()->listen() ;調用listen()後把未連接的套接字變爲被動套接字,只負責監聽,狀態由CLOSED->LISTEN狀態。

2、TCP/IP協議爲什麼要三次握手,和四次斷開?

TCP/IP是全雙工、雙通道協議,既可以收也可以發,是一個雙向認證的
過程,雙方必須確保對端確實收到了自己發送的包,這樣全雙工通道完全
建立起來了。 先調用close的那一端,最終socket狀態推進到TIME_WAIT
狀態,會等一會再close,這裏使用了IO複用技術。

3、爲什麼主動關閉socket的那一端需要進入TIME_WAIT狀態而不是直接進入CLOSED狀態?

因爲要給對等方一個確認,但是在公網上有可能這個確認不是那麼順利,出現了網絡擁塞和異常處理,此時不把先close的一方推到CLOSED狀態,是因爲讓確認包在異常的情況下進行幾次重新發送,保證對方真正的進入CLOSED狀態,然後釋放該釋放的資源。就是讓最後一個確認包在網絡不通暢的情況下真正的進入確認狀態。
注意:
TCP/IP協議是雙通道的,客戶端關閉與服務端關閉沒有任何的關係,TCP/IP是一個流協議,Server端調用read()返回0,則通知對方寫斷了,A端關閉了socket並不代表B端不能往socket寫數據,頂多寫失敗了。linux內核也是有緩衝區的(發送緩衝區、接收緩衝區),緩衝區達到了低水位纔會往對端發送數據。

TIME_WAIT時間是2MSL,(2倍的最大生命期時間),原因是(ACK y+1) 如果發送失敗可以重發。服務端處於close狀態,不等於客戶端也處於close狀態。

4、TCP/IP爲什麼會出現半連接狀態FIN_WAIT_2,socket狀態不往下推進呢?

因爲對等方沒有顯示的調用close,服務器掛掉也相當於顯示的調用close函數,若A端一直是FIN_WAIT_2,需要查B端爲什麼沒有調用close函數,有可能是阻塞在某一函數上,也有可能忘記了close。

5、第11種狀態CLOSING

任一對等方都可以顯示的調用close(),當雙方同時調用close時,這時就會出現第11種狀態CLOSING。客戶端 服務端同時調用close時,TCP/IP協議能檢測出來,因爲都需要對方的確認,此時TCP/IP協議將狀態置爲短暫的CLOSING。當收到對方的確認時,雙方都進入到了TIME_WAIT狀態,因爲雙方都主動的調用了close()函數,都進入到了TIME_WAIT狀態,經過n時間後,雙方都消失。

6、TCP/IP是一個流協議,FIN DCBA 即send(fd,“ABCD”);然後調用close(),則對端一定能收到數據麼?

對方能收到,而且是保證可靠的能收到。因爲tcp/ip協議是一個流協議。清空緩衝區纔會調用close。數據包在緩存區裏面,調用close函數,TCP/IP就會處理緩衝區,將數據推到對等方,最後有個FIN也會跑到對等方緩衝區裏面,然後對等方一個一個的收報文,當收到FIN後,發現是一個流水結束符,此時檢測到了對方的socket已經關閉了,此時把數據給到用戶空間,至此,TCP/IP的工作已經全部做完,然後用戶程序處理。
TCP/IP能保證"ABCD"給到對端,而且也能保證告訴對端socket已經關閉了。

通過程序觀察TCP/IP的各種狀態

演示1:多個客戶端與單個服務器(不支持併發)連接,此時多個客戶端仍然可以連接上服務器
在這裏插入圖片描述

在這裏插入圖片描述

爲什麼3個客戶端都能與單服務器建立連接,但只有一個客戶端能通信?
	答:因爲accept函數放在了for循環的上邊,這樣主進程只能發包收包,不能accept
	沒有機會調用accept 即 沒有機會從已完成三次握手的隊列中拿到連接,所以不支持
	併發,只支持單連接。正確的做法是將accept放在循環裏面

代碼實現請看:
https://blog.csdn.net/WUZHU2017/article/details/82784995
演示2:還是用上面的代碼
(1)啓動服務端,然後啓動客戶端,狀態如下
在這裏插入圖片描述

(2)現在ctrl+ c掉服務端

殺死server,相當於服務器主動的close,然後對等方收到FIN後,TCP/IP協議在上層應用不知道的情況下向對方發了一個確認包

在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章