TCP/IP三次我手四次揮手過程與對應狀態轉移圖

作爲互聯網通訊的基礎,TCP/IP協議建立連接與斷開連接的過程非常重要,但是平時寫代碼的時候很難去關注到這些知識,今天恰好想去整理一下相關知識,發現了下面兩張圖比較好的解釋了三次握手和四次揮手的過程,以及客戶端和服務端相應的狀態切換過程。

TCP/IP協議建立連接的三次握手

  • 客戶端發送一個帶SYN標誌的TCP報文(報文1)到服務器端,表示希望建立一個TCP連接。
  • 服務器發送一個帶ACK標誌和SYN標誌的TCP報文(報文2)給客戶端,ACK用於對報文1的迴應,SYN用於詢問客戶端是否準備好進行數據傳輸。
  • 客戶端發送一個帶ACK標誌的TCP報文(報文3),作爲報文2的迴應。

斷開連接的四次揮手

由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。原則是主動關閉的一方(如已傳輸完所有數據等原因)發送一個FIN報文來表示終止這個方向的連接,收到一個FIN意味着這個方向不再有數據流動,但另一個方向仍能繼續發送數據,直到另一個方向也發送FIN報文。四次揮手的具體過程如下:

  • 客戶端發送一個FIN報文(報文4)給服務器,表示我將關閉客戶端到服務器端這個方向的連接。
  • 服務器收到報文4後,發送一個ACK報文(報文5)給客戶端,序號爲報文4的序號加1。
  • 服務器發送一個FIN報文(報文6)給客戶端,表示自己也將關閉服務器端到客戶端這個方向的連接。
  • 客戶端收到報文6後,發回一個ACK報文(報文7)給服務器,序號爲報文6的序號加1。

連接狀態

客戶端TCP狀態遷移:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

服務器TCP狀態遷移:

CLOSED->LISTEN->SYN_RECEIVED->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

下面是每一個TCP連接在任意時刻可能處於的狀態,在Linux下可以在netstat命令的最後一列(State列)裏看到。
各個狀態的含義如下:

  • CLOSED:初始狀態,表示TCP連接是“關閉着的”或“未打開的”。
  • LISTEN :表示服務器端的某個SOCKET處於監聽狀態,可以接受客戶端的連接。
  • SYN_RCVD :表示接收到了SYN報文。在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一箇中間狀態,很短暫,基本上用netstat很難看到這種狀態,除非故意寫一個監測程序,將三次TCP握手過程中最後一個ACK報文不予發送。當TCP連接處於此狀態時,再收到客戶端的ACK報文,它就會進入到ESTABLISHED 狀態。
  • SYN_SENT :這個狀態與SYN_RCVD 狀態相呼應,當客戶端SOCKET執行connect()進行連接時,它首先發送SYN報文,然後隨即進入到SYN_SENT 狀態,並等待服務端的發送三次握手中的第2個報文。SYN_SENT 狀態表示客戶端已發送SYN報文。
  • ESTABLISHED :表示TCP連接已經成功建立。
  • FIN_WAIT_1 :這個狀態得好好解釋一下,其實FIN_WAIT_1 和FIN_WAIT_2 兩種狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:- FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連接,向對方發送了FIN報文,此時該SOCKET進入到FIN_WAIT_1 狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2 狀態。當然在實際的正常情況下,無論對方處於任何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1 狀態一般是比較難見到的,而FIN_WAIT_2 狀態有時仍可以用netstat看到。
  • FIN_WAIT_2 :上面已經解釋了這種狀態的由來,實際上FIN_WAIT_2狀態下的SOCKET表示半連接,即有一方調用close()主動要求關閉連接。注意:FIN_WAIT_2 是沒有超時的(不像TIME_WAIT 狀態),這種狀態下如果對方不關閉(不配合完成4次揮手過程),那這個 FIN_WAIT_2 狀態將一直保持到系統重啓,越來越多的FIN_WAIT_2 狀態會導致內核crash。
  • TIME_WAIT :表示收到了對方的FIN報文,併發送出了ACK報文。 TIME_WAIT狀態下的TCP連接會等待2*MSL(Max Segment Lifetime,最大分段生存期,指一個TCP報文在Internet上的最長生存時間。每個具體的TCP協議實現都必須選擇一個確定的MSL值,RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本機的這個值),然後即可回到CLOSED 可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
  • CLOSING :這種狀態在實際情況中應該很少見,屬於一種比較罕見的例外狀態。正常情況下,當一方發送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING 狀態表示一方發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?那就是當雙方幾乎在同時close()一個SOCKET的話,就出現了雙方同時發送FIN報文的情況,這是就會出現CLOSING 狀態,表示雙方都正在關閉SOCKET連接。
  • CLOSE_WAIT :表示正在等待關閉。怎麼理解呢?當對方close()一個SOCKET後發送FIN報文給自己,你的系統毫無疑問地將會迴應一個ACK報文給對方,此時TCP連接則進入到CLOSE_WAIT狀態。接下來呢,你需要檢查自己是否還有數據要發送給對方,如果沒有的話,那你也就可以close()這個SOCKET併發送FIN報文給對方,即關閉自己到對方這個方向的連接。有數據的話則看程序的策略,繼續發送或丟棄。簡單地說,當你處於CLOSE_WAIT 狀態下,需要完成的事情是等待你去關閉連接。
  • LAST_ACK :當被動關閉的一方在發送FIN報文後,等待對方的ACK報文的時候,就處於LAST_ACK 狀態。當收到對方的ACK報文後,也就可以進入到CLOSED 可用狀態了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章