TCP狀態轉移
在前一篇文章【面試必考】TCP協議“三次握手”與“四次揮手”已經介紹了TCP
協議的三次握手和四次揮手。總的來說,TCP通信過程包括三個步驟:建立TCP連接(三次握手)、數據傳輸、終止TCP連接(四次揮手)。但是在這個通信過程中,有非常復雜的狀態問題,下面就來了解一下進行TCP協議通信時候的狀態轉移。
TCP協議根據連接時接收到報文的不同類型,采取相應動作也不同,還要處理各個狀態的關系,如當收到握手報文時候、超時的時候、用戶主動關閉的時候等都需要不一樣的狀態去采取不一樣的處理。在LwIP中,為了實現TCP
協議的狀態描述,定義了11種連接時候的狀態:
1static const char *const tcp_state_str[] = {
2 "CLOSED", //關閉狀態(無連接)
3 "LISTEN", //監聽狀態
4 "SYN_SENT", //已發起請求連接(等待確認)
5 "SYN_RCVD", //已收到請求連接
6 "ESTABLISHED",//穩定連接狀態
7 "FIN_WAIT_1", //單向請求終止連接狀態
8 "FIN_WAIT_2", //對方已應答請求終止連接
9 "CLOSE_WAIT", //等待終止連接
10 "CLOSING", //兩端同時關閉
11 "LAST_ACK", //服務器等待對方接受關閉
12 "TIME_WAIT" //關閉成功(2MSL等待狀態)
13};
LISTEN
:表示監聽狀態。服務器
調用了listen函數進入監聽狀態,客戶端可以開始進行連接了。SYN_SENT
:表示客戶端已經發送了SYN
報文請求連接(同時在等待服務器的確認)。當客戶端調用connect
函數發起連接時,首先發SYN給服務端,然后自己進入SYN_SENT
狀態,并等待服務端發送ACK+SYN
報文(握手應答報文)進行確認。SYN_RCVD
:在每一個 TCP 連接建立時,都要進行三次握手,這個狀態表示服務器
接收到客戶端發來的同步報文段(第一次握手),并且向客戶端發送了確認同步報文段(第二次握手)之后的狀態,在這個狀態時,其實連接已經經歷了兩次握手。ESTABLISHED
:這個狀態是處于穩定連接狀態,建立連接的TCP協議兩端的主機都是處于這個狀態,它們相互知道彼此的窗口大小、序列號、最大報文段等信息。FIN_WAIT_1
與FIN_WAIT_2
:處于這個狀態一般都是客戶端主機
單向請求終止連接,然后主機等待服務器
的回應,而如果服務器產生應答,則主機狀態轉移為FIN_WAIT_2
,此時<客戶端 -> 服務器
>方向上的TCP連接就斷開,但是<服務器 -> 客戶端
>方向上的連接還是存在的。此處有一個注意的地方:如果主機處于FIN_WAIT_2
狀態,說明主機已經發出了FIN
報文段,并且服務器也已對它進行確認,除非客戶端是在實行半關閉狀態,否則將等待服務器主機的應用層處理關閉連接,因為服務器已經意識到它已收到FIN
報文段,它需要發一個 FIN 來關閉<服務器 -> 客戶端
>方向上的連接。這樣客戶端這端才會從FIN_WAIT_2
狀態進入TIME_WAIT
狀態。如果是網絡不好或者是服務器不發送FIN
報文段的時候,這意味著客戶端這端可能永遠保持這個FIN_WAIT_2
狀態,從而無法 進入CLOSE_WAIT
狀態,并一直占用這個端口連接或者socket
,在嵌入式中,如果存在多個這種狀態的話,則這很可能導致內存耗盡。CLOSE_WAIT
:在收到客戶端
主動斷開連接的 FIN 報文段(第一次揮手)后,服務器
返回給客戶端確認報文段(第二次揮手)后的狀態。TIME_WAIT
狀態:TIME_WAIT
狀態也稱為2MSL等待狀態
。
具體見下圖:
-
.
紅色
虛線:表示服務器的狀態轉移。 -
. 黑色實線:表示客戶端的狀態轉移。
TCP協議狀態轉移
RST報文
順便再提一點不太常見的TCP協議狀態轉移,主要是針對服務器端的(綠色那條):
- 服務器在收到
SYN
握手報文后,再收到了客戶端的RST
報文,那么它會重新進入監聽狀態,再重新等待連接。
一般說來,無論何時一個報文段發往基準的連接出現錯誤, TCP
都會發出一個復位報文段(這里提到的基準的連接
是指由目的 IP地址、目的端口號、源 IP地址和源端口號都是已知
的連接。
此外產生復位的另一種常見情況是當連接請求到達時,目的端口并沒有在監聽中,當一個數據報到達目的端口時,它將產生一個ICMP
端口不可達的信息,同時TCP協議將進行復位,當然啦,在lwip中這些ICMP
端口不可達報文都會被丟棄的,也不用管那么多。
TIME_WAIT狀態
第一次看這個轉移圖的時候,可能很多人都有疑惑,為什么要有一個 TIME_WAIT
狀態?為什么不能直接到達 CLOSED
狀態?
每個具體TCP
連接的實現必須選擇一個TCP
報文段最大生存時間MSL
(Maximum Segment Lifetime),就如IP數據報
中的TTL
字段表示報文在網絡中生存的時間一樣。MSL
是任何報文段被丟棄前在網絡內的最長時間,這個時間是有限的,為什么需要等待呢?我們知道 IP數據報 是不可靠的,而TCP報文段是封裝在IP數據報中,TCP協議必須保證發出的 ACK 報文段是正確被對方接收, 因此處于該狀態的主機必須在這個狀態停留最長時間為2倍的MSL,以防最后這個ACK丟失,因為TCP協議必須保證數據能準確送達目的地。
我們來假設一下 :假設沒有 TIME_WAIT
這種狀態。現實中,網絡環境不是理想的。在數據包傳輸的過程中,難免會有一些延時啊、丟包啊
的情況發生。如果在客戶端的最后一個確認報文段
發出去之后,由于某種原因,沒有到達服務端,服務端在超時后,就會向客戶端重新發一個 FIN 報文段,請求重傳這個已經丟失的確認報文段
。但由于在客戶端,連接實際上已經斷開,端口已經關閉。那么在客戶端收到這個報文段后,會向服務端發送一個 RST
報文段請求重連(這也是為什么我要在前面講解RST
的原因 ),而此時服務器收到這個 RST
報文段后,會認為是錯誤的,因為在服務器看來都沒斷開連接,它所期望收到的是確認報文段。所以這個時候客戶端是不允許直接CLOSE
關閉了事的,因此它需要等待服務器確認了,再CLOSE
。
再假設一下:如果沒有 TIME_WAIT
這種狀態,客戶端在關閉連接后,再次成功建立新的連接
,客戶端任然可能會收到服務器的最后一個確認報文段
,但是由于序號
不同(重新建立連接時的序號是隨機
的,這點很重要,要記住),客戶端會要求服務端重傳
數據包,這樣,連接就必然會混亂出錯。而在 TIME_WAIT
這種狀態等待一段時間是為了讓本次連接的時間內所產生的所有報文都從網絡中消失,使得下一個新的連接不會出現舊的報文。
而 TIME_WAIT
狀態的等待時間一般是 2MAL
,并且客戶端連接的端口沒有釋放,這樣,讓前一個連接的報文段有足夠的時間被處理或者丟棄,也就不會出現這個問題。
這才是TCP協議優雅且可靠
的終止連接方式啊!太強大了,我得膜拜一下~
-
狀態
+關注
關注
0文章
16瀏覽量
11974 -
LwIP
+關注
關注
2文章
88瀏覽量
27371 -
TCP協議
+關注
關注
1文章
101瀏覽量
12124
發布評論請先 登錄
相關推薦
評論