1. 程式人生 > >TCP狀態圖的理解以及TIME_WAIT狀態的作用

TCP狀態圖的理解以及TIME_WAIT狀態的作用

TCP/IP狀態圖 以及 TIME_WAIT作用

在TCP/IP狀態圖中,有很多種的狀態,它們之間有的是可以互相轉換的,也就是說,從一種狀態轉到另一種狀態,但是這種轉換不是隨便傳送的,是要滿足一定的條件。TCP/IP狀態圖看起來更像是自動機。下圖即為TCP/IP狀態。

由上圖可以看出,一共有11種不同的狀態。這11種狀態描述如下:

CLOSED:關閉狀態,沒有連線活動或正在進行;

LISTEN:監聽狀態,伺服器正在等待連線進入;

SYN_RCVD:收到一個連線請求,尚未確認;

SYN_SENT:已經發出連線請求,等待確認;

ESTABLISHED:連線建立,正常資料傳輸狀態;

FIN_WAIT 1:(主動關閉)已經發送關閉請求,等待確認;

FIN_WAIT 2:(主動關閉)收到對方關閉確認,等待對方關閉請求;

TIME_WAIT:完成雙向關閉,等待所有分組死掉;

CLOSING:雙方同時嘗試關閉,等待對方確認;

CLOSE_WAIT:(被動關閉)收到對方關閉請求,已經確認;

LAST_ACK:(被動關閉)等待最後一個關閉確認,並等待所有分組死掉。

在這11中狀態當中,TIME_WAIT這種狀態是最重要的,也是最難理解的。

1.CLOSED:起始點,在超時或者連線關閉時候進入此狀態。

2.LISTEN:服務端在等待連線過來時候的狀態,服務端為此要呼叫socket,bind,listen函式,就能進入此狀態。此稱為應用程式被動開啟(等待客戶端來連線)。

3.SYN_SENT:客戶端發起連線,傳送SYN給伺服器端。如果伺服器端不能連線,則直接進入CLOSED狀態。

4.SYN_RCVD:跟3對應,伺服器端接受客戶端的SYN請求,伺服器端由LISTEN狀態進入SYN_RCVD狀態。同時伺服器端要回應一個ACK,同時傳送一個SYN給客戶端;另外一種情況,客戶端在發起SYN的同時接收到伺服器端得SYN請求,客戶端就會由SYN_SENT到SYN_RCVD狀態。

5.ESTABLISHED:伺服器端和客戶端在完成3次握手進入狀態,說明已經可以開始傳輸資料了。

以上是建立連線時伺服器端和客戶端產生的狀態轉移說明。相對來說比較簡單明瞭,如果你對三次握手比較熟悉,建立連線時的狀態轉移還是很容易理解。

下面,我們來看看連線關閉時候的狀態轉移說明,關閉需要進行4次雙方的互動,還包括要處理一些善後工作(TIME_WAIT狀態),注意,這裡主動關閉的一方或被動關閉的一方不是指特指伺服器端或者客戶端,是相對於誰先發起關閉請求來說的:

6.FIN_WAIT_1:主動關閉的一方,由狀態5進入此狀態。具體的動作是傳送FIN給對方。

7.FIN_WAIT_2:主動關閉的一方,接收到對方的FIN-ACK(即fin包的迴應包),進入此狀態。

8.CLOSE_WAIT:接收到FIN以後,被動關閉的一方進入此狀態。具體動作是接收到FIN,同時傳送ACK。(之所以叫close_wait可以理解為被動關閉方此時正在等待上層應用發出關閉連線指令)

9.LAST_ACK:被動關閉的一方,發起關閉請求,由狀態8進入此狀態。具體動作是傳送FIN給對方,同時在接收到ACK時進入CLOSED狀態。

10.CLOSING:兩邊同時發起關閉請求時,會由FIN_WAIT_1進入此狀態。具體動作是接收到FIN請求,同時響應一個ACK。

11.TIME_WAIT:最糾結的狀態來了。從狀態圖上可以看出,有3個狀態可以轉化成它,我們一一來分析:

  • a.由FIN_WAIT_2進入此狀態:在雙方不同時發起FIN的情況下,主動關閉的一方在完成自身發起的關閉請求後,接收到被動關閉一方的FIN後進入的狀態。

  • b.由CLOSING狀態進入:雙方同時發起關閉,都做了發起FIN的請求,同時接收到了FIN並做了ACK的情況下,由CLOSING狀態進入。

  • c.由FIN_WAIT_1狀態進入:同時接受到FIN(對方發起),ACK(本身發起的FIN迴應),與b的區別在於本身發起的FIN迴應的ACK先於對方的FIN請求到達,而b是FIN先到達。這種情況概率最小。

為什麼需要 TIME_WAIT 狀態

假設最終的 ACK 丟失 , server 將重發 FIN , client 必須維護 TCP 狀態資訊以便可以重發最終的 ACK ,否則會發送RST ,結果 server 認為發生錯誤。 TCP 實現必須可靠地終止連線的兩個方向 ( 全雙工關閉 ) , client 必須進 TIME_WAIT狀態,因為 client 可能面臨重發最終 ACK 的情形。先呼叫 close() 的一方會進入 TIME_WAIT 狀態

為什麼 TIME_WAIT 狀態需要保持 2MSL 的時間

如果 TIME_WAIT 狀態保持時間不足夠長 ( 比如小於 2MSL) ,第一個連線就正常終止了。 第二個擁有相同相關五元組的連接出現,而第一個連線的重複報文到達,干擾了第二個連線。 TCP 實現必須防止某個連線的重複報文在連線終止後出現,所以讓 TIME_WAIT 狀態保持時間足夠長 (2MSL) ,連線相應方向上的 TCP 報文要麼完全響應完畢,要麼被丟棄。建立第二個連線的時候,不會混淆。

根據《TCP/IP詳解》中的TCP的建立和終止中有關"TCP的終止"的講解

TCP的終止通過雙方的四次握手實現。發起終止的一方執行主動關閉,響應的另一方執行被動關閉。

發起方更改狀態為FIN_WAIT_1,關閉應用程式程序,發出一個TCP的FIN段;

接收方收到FIN段,返回一個帶確認序號的ACK,同時向自己對應的程序傳送一個檔案結束符EOF,同時更改狀態為CLOSE_WAIT,發起方接到 ACK後狀態更改為FIN_WAIT_2;

接收方關閉應用程式程序,更改狀態為LAST_ACK,並向對方發出一個TCP的FIN段;

發起方接到FIN後狀態更改為TIME_WAIT,併發出這個FIN的ACK確認。ACK傳送成功後(2MSL內)雙方TCP狀態變為CLOSED。

我們不難看出上面的顯示的結果的意思。根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態(TCP實現必須可靠地終止連線的兩個方向(全雙工關閉)),持續2*MSL (Max Segment Lifetime),預設為240秒。

TIME_WAIT狀態的作用

主動關閉的Socket端會進入TIME_WAIT狀態,並且持續2MSL時間長度,MSL就是maximum segment lifetime(最大分節生命期),這是一個IP資料包能在網際網路上生存的最長時間,超過這個時間將在網路中消失。MSL在RFC 1122上建議是2分鐘,而源自berkeley的TCP實現傳統上使用30秒,因而,TIME_WAIT狀態一般維持在1-4分鐘。

TIME_WAIT狀態存在的理由

1)可靠地實現TCP全雙工連線的終止

在進行關閉連線四路握手協議時,最後的ACK是由主動關閉端發出的,如果這個最終的ACK丟失,伺服器將重發最終的FIN,因此客戶端必須維護狀態資訊允 許它重發最終的ACK。如果不維持這個狀態資訊,那麼客戶端將響應RST分節,伺服器將此分節解釋成一個錯誤(在java中會丟擲connection reset的SocketException)。因而,要實現TCP全雙工連線的正常終止,必須處理終止序列四個分節中任何一個分節的丟失情況,主動關閉 的客戶端必須維持狀態資訊進入TIME_WAIT狀態。

2)允許老的重複分節在網路中消逝

TCP分節可能由於路由器異常而“迷途”,在迷途期間,TCP傳送端可能因確認超時而重發這個分節,迷途的分節在路由器修復後也會被送到最終目的地,這個 原來的迷途分節就稱為lost duplicate。在關閉一個TCP連線後,馬上又重新建立起一個相同的IP地址和埠之間的TCP連線,後一個連線被稱為前一個連線的化身 (incarnation),那麼有可能出現這種情況,前一個連線的迷途重複分組在前一個連線終止後出現,從而被誤解成從屬於新的化身。為了避免這個情 況,TCP不允許處於TIME_WAIT狀態的連線啟動一個新的化身,因為TIME_WAIT狀態持續2MSL,就可以保證當成功建立一個TCP連線的時 候,來自連線先前化身的重複分組已經在網路中消逝。

本部落格文章除特別宣告,全部都是原創!

尊重原創,轉載請註明: 轉載自過往記憶(http://www.iteblog.com/)

本文連結地址: 《TCP/IP狀態圖的TIME_WAIT作用》(http://www.iteblog.com/archives/169)