讀懂TCP狀態轉移


讀懂TCP狀態轉移過程,對理解網絡編程頗有幫助,本文將對TCP狀態轉移過程進行介紹,但各狀態(總共11個)含義不在本文介紹的范圍,請參考文末的書目列表。

TCP狀態轉換圖(state transition diagram)

狀態轉換圖

1. 建立連接(three-way hand shake)

  • 主動打開(passive open):服務器必須准備好接受外來的連接,通常通過socket、bind和listen完成。
  • 被動打開(active open):客戶端通過connect發起主動打開。

插句話,該文介紹的性能分析工具(sar -n TCP,ETCP 1)命令可以對主動打開和被動打開的數目進行統計,在一定程度上可以反應服務器的繁忙程度。

下圖給出客戶端與服務器的三次握手過程:

三次握手過程

  1. 服務器准備好接受外來連接,通常通過socket、bind和listen完成。(服務器:CLOSED->LISTEN)
  2. 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器客戶端將在待建立連接發送數據的初始序列號。(客戶端:CLOSED->SYN_SENT)
  3. 服務器端必須ACK客戶端SYN,同時發送一個SYN,告訴客戶端,服務器將在待建立連接發送數據的初始序列號。(服務器:SYN_RCVD)
  4. 客戶端必須ACK服務器SYN。(客戶端:SYN_SENT->ESTABLISHED)
  5. 服務器接收到客戶端ACK。(服務器:SYN_RECV->ESTABLISHED)

下圖給出三次握手對應的狀態轉移圖:

狀態轉移圖

2. 建立連接(同時打開simultaneous open)

參考《TCP/IP詳解》卷一第18章18.8節。這種情況發生在兩端幾乎同時發送SYN並且這兩個SYN在網絡中交錯的情形。這種情況可能發生,但是非常罕見。

例如,主機A的應用程序使用本地端口7777,並與主機B的端口8888執行主動打開。主機B的應用程序使用本地端口8888,並與主機A的端口7777執行主動打開。

下圖給出同時打開過程:

同時打開過程

下圖給出同時打開的狀態轉移圖:

狀態轉換圖

說明:從目前個人經驗來看,這種場景沒有遇到過,但這是完整理解TCP狀態轉移必須的一部分。但《TCP/IP詳解》卷一第18章18.8節中支出,許多伯克利版的TCP實現都不能正確地支持打開。

3. 建立連接失敗(1)

考慮場景:客戶端尚未接收到服務器ACK+SYN,異常退出(崩潰退出)。這時,當客戶端接收到服務器的ACK+SYN時,客戶端回復RST(reset)。此時服務器處於SYN_RCVD狀態,當接收到客戶端RST時,則從SYN_RCVD轉移到LISTEN狀態。

具體過程:

  1. 服務器准備好接受外來連接,通常通過socket、bind和listen完成。(服務器:CLOSED->LISTEN)
  2. 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器,客戶端將在待建立連接發送數據的初始序列號。然后客戶端異常退出。(客戶端:CLOSED->SYN_SENT,然后突然crash,則退出SYN_SENT)
  3. 服務器端ACK客戶端SYN,同時發送一個SYN,告訴客戶端,服務器將在待建立連接發送數據的初始序列號。(服務器:SYN_RCVD)
  4. 客戶端找不到服務器ACK+SYN對應的SYN_SENT狀態的socket,則響應RST。(服務器:SYN_RCVD->LISTEN)

下圖給出建立連接失敗的狀態轉移圖:

狀態轉換圖

4. 建立連接失敗(2)

考慮場景1:服務器的進程異常退出,客戶端不知道。那么客戶端發送SYN后,服務器端響應RST,則客戶端建立連接失敗。

考慮場景2:服務器機器關閉,導致服務器IP不可達。那么客戶端發送SYN后,超時重發,超過重試次數,最終TIMEOUT,則客戶端建立連接失敗。

具體過程:

  1. 假設服務器進程退出。(服務器:LISTEN->CLOSED)
  2. 客戶端通過connect連接服務器,客戶端TCP將發送一個SYN包,告訴服務器,客戶將在待建立連接發送數據的初始序列號。(客戶端:CLOSED->SYN_SENT)
  3. 服務器端收到客戶端SYN,響應RST(服務器:CLOSED)
  4. 客戶收到RST。(客戶端:SYN_SENT->CLOSED)

下圖給出建立連接失敗的狀態轉移圖:

狀態轉換圖

5. 斷開連接(四次揮手)

  • 主動關閉(active close):某個應用程序首先調用close,發送一個FIN包。
  • 被動關閉(passive close):接收到FIN的對端執行被動關閉。

下圖給出客戶端與服務器的四次揮手過程:

四次揮手

  1. 某個應用程序首先調用close,該端發送一個FIN包,表示數據發送完畢,該應用程序再無更多數據發送給對端。(例如HTTP服務器發送Reponse數據給client后,再無多余數據發送,則Server可以執行主動關閉)(主動端:ESTABLISHED->FIN_WAIT_1)
  2. 接收到FIN的對端執行被動關閉。首先ACK這個收到的FIN包。該FIN包的接收也作為一個文件結束符(EOF)傳遞給應用程序(放在已排隊等候該應用進程接收的任何其他數據之后),因為FIN包意味着接收端應用進程在相應的連接上再無額外數據可接收。(被動端:ESTABLISHED->CLOSE_WAIT,主動端:FIN_WAIT_1->FIN_WAIT_2)
  3. 一段時間后,接收到這個EOF的應用進程將調用close關閉socket。這導致它的TCP也發送一個FIN包。(被動端:CLOSE_WAIT->LAST_WAIT)
  4. 接收這個最終FIN的執行主動關閉的那一端ACK這個FIN。(被動端:LAST_WAIT->CLOSED,主動端:FIN_WAIT_2->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))

下圖給出四次揮手的狀態轉移圖:

狀態轉換圖

6. 斷開連接(同時關閉simultaneous close)

參考《TCP/IP詳解》卷一第18章18.9節。我們在前面討論了一方(通常但不總是客戶方)發送第一個FIN執行主動關閉。雙方都執行主動關閉也是可能的,TCP協議也允許這樣的同時關閉(simultaneous close)。

下圖給出同時關閉過程:

同時打開過程

下圖給出同時關閉的狀態轉移圖:

狀態轉換圖

7. 斷開連接(在FIN_WAIT_1狀態中,接收FIN+ACK)

考慮場景:被動關閉端收到FIN包后,直接發送FIN+ACK,則主動關閉方則從FIN_WAIT_1跳過FIN_WAIT_2,直接進入TIME_WAIT。

給出具體流程:

  1. 某個應用程序首先調用close,該端發送一個FIN包,表示數據發送完畢,該應用程序再無更多數據發送給對端。(例如HTTP服務器發送Reponse數據給client后,再無多余數據發送,則Server可以執行主動關閉)(主動端:ESTABLISHED->FIN_WAIT_1)
  2. 接受到FIN的對端執行被動關閉。收到FIN包之后,被動端調用close關閉socket,則FIN+ACK同時發給主動端。(被動端:ESTABLISHED->CLOSE_WAIT->LAST_ACK,主動端:FIN_WAIT_1->TIME_WAIT)
  3. 接收這個最終FIN的執行主動關閉的那一端ACK這個FIN。(被動端:LAST_WAIT->CLOSED,主動端:FIN_WAIT_1->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))

下圖給出同時關閉的狀態轉移圖:

狀態轉換圖

參考:

  1. 中文版《UNIX網絡編程》第一卷第2章
  2. 中文版《TCP/IP詳解》卷一第18章

錯誤說明:

  1. 中文版《UNIX網絡編程》第一卷(第三版)(第一次印刷版)圖2.4有個錯誤,服務器從LISTEN轉移到SYN_RCVD時,條件應該是“接收:SYN;發送:SYN,ACK”,而不是“接收:RST;發送:SYN,ACK”。這一點可以從英文原本的配圖看到。(注:2015年8月第二次印刷已經修訂這個錯誤)
  2. 我也認為,書中的TCP狀態轉移圖中,SYN_RCVD轉移到LISTEN的條件應該是服務器狀態,而非客戶端狀態

糾錯

關於作者:

限於本人對某些技術點的理解程度(雖然也查了很多資料),可能某些地方表達的不太准確,敬請指教,聯系方式為pengfeicui@yeah.net。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM