歡迎訪問我的個人網(wǎng)站獲取更佳排版體驗: https://pengrl.com/p/49801

本文包含內(nèi)容:
- TCP FIN 簡介
- TCP ACK 簡介
- 為什么 TCP 連接關(guān)閉是四次揮手
- 先看最常見的正常關(guān)閉流程
- 什么時候進入
CLOSING狀態(tài) -
TIME_WAIT狀態(tài)的作用 - 為什么
被動關(guān)閉端不需要維持一個類似TIME_WAIT的狀態(tài),萬一發(fā)出的 ACK 也丟失了呢 - 最后,再奉上一張 geek 味十足的tcp狀態(tài)遷移圖
TCP FIN 簡介
FIN可以理解為TCP包中的一種信令類型。事實上,F(xiàn)IN是TCP包頭中的一個固定標志位,該標志位為1時,表示本端關(guān)閉連接。
TCP ACK 簡介
TCP基于IP協(xié)議,IP包不保證可靠傳輸。
TCP的做法是,發(fā)送端發(fā)送的每個TCP包攜帶序號信息,接收端通過序號處理亂序包和重復包。
并且,接收端持續(xù)向發(fā)送端發(fā)送ACK信息,反饋接收端接收到的流式數(shù)據(jù)的位置,從而使得發(fā)送端有能力檢測出對端未接收到數(shù)據(jù),發(fā)送端可重新發(fā)送丟失的數(shù)據(jù)。
為什么 TCP 連接關(guān)閉是四次揮手
由于TCP可雙向發(fā)送數(shù)據(jù),所以TCP連接關(guān)閉至少需要兩次揮手,即兩端各自發(fā)送FIN,表示本端已無數(shù)據(jù)需要發(fā)送。
又由于FIN包也有丟失的可能,所以出現(xiàn)了ACK of FIN,即對FIN包也回復ACK確認,以保證FIN包丟失后可以重傳。
事實上,由于FIN和ACK of FIN都有可能丟失,TCP連接關(guān)閉的設計比四次揮手通常被理解的情況要更復雜些。
我們接下來看。
先看最常見的正常關(guān)閉流程
提示,本文的后續(xù)內(nèi)容建議結(jié)合上面的TCP連接關(guān)閉狀態(tài)連接圖配套食用。
以下主動關(guān)閉端指未收到對端FIN的情況下本端發(fā)送FIN。被動關(guān)閉端則是先收到對端發(fā)送的FIN,之后某個時間點本端發(fā)送FIN。
主動關(guān)閉端
流程為: 發(fā)送FIN,接收ACK,接收FIN,發(fā)送ACK
狀態(tài)遷移為: ESTAB -> FINWAIT_1 -> FINWAIT_2 -> TIME_WAIT -> CLOSED
被動關(guān)閉端
流程為: 接收FIN,發(fā)送ACK,發(fā)送FIN,接收ACK
狀態(tài)遷移為: ESTAB -> CLOSE_WAIT -> LAST_ACK -> CLOSED
什么時候進入 CLOSING 狀態(tài)
有兩種情況:
第一種,由于ACK of FIN可能丟失,所以當被動關(guān)閉端發(fā)出的ACK丟失,而被動關(guān)閉端緊接著又發(fā)出了FIN,那么主動關(guān)閉端會先收到FIN,從而進入CLOSING狀態(tài)。
第二種,兩端都在還未收到FIN的的情況,發(fā)出了FIN,即兩端同時關(guān)閉,那么可能出現(xiàn)發(fā)出FIN后還沒收到對應的ACK,立馬就收到對端發(fā)出的FIN。第二種情況下,兩端都是主動關(guān)閉端,它們都是首先從ESTAB狀態(tài)進入FINWAIT_1狀態(tài),不再走CLOSE_WAIT那條狀態(tài)遷移路線。
TIME_WAIT 狀態(tài)的作用
簡單來說,由于ACK of FIN存在丟失的可能。主動關(guān)閉端向被動關(guān)閉端回復的ACK of FIN可能丟失(此ACK為主動關(guān)閉端發(fā)出的最后一個包)。而TCP是不會再去對ACK做ACK確認的。
那么如果出現(xiàn)主動關(guān)閉端發(fā)出的ACK丟失了的情況,被動關(guān)閉端由于收不到ACK將停留在LAST_ACK狀態(tài),無法進入CLOSED狀態(tài)。
TCP協(xié)議處理這種異常情況的做法是:被動關(guān)閉端如果收不到ACK將觸發(fā)超時重新發(fā)送FIN;在主動關(guān)閉端維持一個TIME_WAIT,繼續(xù)接收FIN并作出ACK回復。
TIME_WAIT的維持時間是2個MSL,MSL是Maximum Segment Lifetime的縮寫,即報文最大生存時間。
其實講到這我們已經(jīng)可以知道,作為主動關(guān)閉端,進入TIME_WAIT狀態(tài)并維持一段時間是TCP協(xié)議本身的設計,并不是一種異常狀態(tài)。
之后我會再單獨針對TIME_WAIT寫一篇文章,描述TIME_WAIT的維持時間為什么是2個MSL,TIME_WAIT的一些內(nèi)核相關(guān)參數(shù)的設置,以及服務器上出現(xiàn)大量TIME_WAIT時我們應該怎么辦。
為什么被動關(guān)閉端不需要維持一個類似 TIME_WAIT 的狀態(tài),萬一發(fā)出的 ACK 也丟失了呢
我在學習TCP連接關(guān)閉時,有一個疑問,既然主動關(guān)閉端為了解決發(fā)送的ACK丟失,維持了一個TIME_WAIT狀態(tài)。
那么被動關(guān)閉端發(fā)出的ACK也可能丟失,如果這個ACK丟失(其他包都未丟失),被動關(guān)閉端會順利進入CLOSED狀態(tài),那么主動關(guān)閉端豈不是會一直停留在CLOSING狀態(tài)。
后來在網(wǎng)上詢問了一些高手,他們的解釋是并不會出現(xiàn)這種情況。
理解這個問題,首先要理解ACK of FIN可以是一個單獨的TCP包,也可以不是。
事實上,ACK信息也是TCP包頭中的一個固定字段。
所以,當被動關(guān)閉端關(guān)閉連接發(fā)送FIN時,F(xiàn)IN中的ACK字段也會攜帶對端發(fā)出FIN所對應的ACK信息。只要主動關(guān)閉端收到了這個FIN,也即可以確認對端已經(jīng)收到了本端發(fā)出的FIN。
這種情況下,可以理解為主動關(guān)閉端在FINWAIT_1收到FIN后,進入CLOSING狀態(tài),之后迅速進入了TIME_WAIT狀態(tài)。
最后,奉上一張geek味十足的tcp狀態(tài)遷移圖
圖片來源: RFC 793 - Transmission Control Protocol (https://tools.ietf.org/html/rfc793)
另外從中可以看到,SYN_RCVD狀態(tài)也可以發(fā)送FIN從而進入FINWAIT_1。
+---------+ ---------\ active OPEN
| CLOSED | \ -----------
+---------+<---------\ \ create TCB
| ^ \ \ snd SYN
passive OPEN | | CLOSE \ \
------------ | | ---------- \ \
create TCB | | delete TCB \ \
V | \ \
+---------+ CLOSE | \
| LISTEN | ---------- | |
+---------+ delete TCB | |
rcv SYN | | SEND | |
----------- | | ------- | V
+---------+ snd SYN,ACK / \ snd SYN +---------+
| |<----------------- ------------------>| |
| SYN | rcv SYN | SYN |
| RCVD |<-----------------------------------------------| SENT |
| | snd ACK | |
| |------------------ -------------------| |
+---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+
| -------------- | | -----------
| x | | snd ACK
| V V
| CLOSE +---------+
| ------- | ESTAB |
| snd FIN +---------+
| CLOSE | | rcv FIN
V ------- | | -------
+---------+ snd FIN / \ snd ACK +---------+
| FIN |<----------------- ------------------>| CLOSE |
| WAIT-1 |------------------ | WAIT |
+---------+ rcv FIN \ +---------+
| rcv ACK of FIN ------- | CLOSE |
| -------------- snd ACK | ------- |
V x V snd FIN V
+---------+ +---------+ +---------+
|FINWAIT-2| | CLOSING | | LAST-ACK|
+---------+ +---------+ +---------+
| rcv ACK of FIN | rcv ACK of FIN |
| rcv FIN -------------- | Timeout=2MSL -------------- |
| ------- x V ------------ x V
\ snd ACK +---------+delete TCB +---------+
------------------------>|TIME WAIT|------------------>| CLOSED |
+---------+ +---------+