文章目錄
- 一、四次揮手各狀態
- FIN_WAIT_1
- CLOSE_WAIT
- FIN_WAIT_2
- LAST_ACK
- TIME_WAIT
- CLOSE
- 二、雙方同時調用close(),FIN_WAIT_1狀態后進入CLOSING狀態
- CLOSING狀態
- 三、TIME_WAIT狀態詳解
- (1) TIME_WAIT狀態下的2MSL是什么
- MSL (報文最大生存時間)
- 為什么TIME__WAIT狀態必須停留兩倍MSL時間
- (2) TIME_WAIT存在的意義
- (3) 為什么會出現大量的TIME_WAIT狀態
- (4) TIME_WAIT狀態的影響
- 解決方法
- 四、HTTP中一般服務端是請求斷開方

由于TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
一、四次揮手各狀態
FIN_WAIT_1
當主動關閉方在ESTABLISHED狀態時發送一個FIN,用來關閉數據傳送,此時即進入到FIN_WAIT_1狀態。
CLOSE_WAIT
處于ESTABILSHED狀態的服務器收到FIN包,將發回一個ACK,確認序號為收到的序號加1。
CLOSE_WAIT: 這種狀態表示在等待關閉。當對方發送FIN報文給自己,系統會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。如果還有數據發送給對方則可以在數據發送結束后再發送FIN報文來結束連接,所以這個狀態是被斷開方再等待自己數據發送結束后斷開連接
FIN_WAIT_2
處于FIN_WAIT_1狀態時,當接收到對方回應的ACK報文后,則進入到FIN_WAIT_2狀態
LAST_ACK
被動關閉一方在發送FIN報文后,最后等待對方的ACK報文期間處于LAST_ACK狀態
TIME_WAIT
time_wait 是「主動關閉 TCP 連接」一方的狀態,可能是==「客服端」的,也可能是「服務器端」==的;
當TCP的一端發起主動關閉(收到 FIN 請求),即第3次握 手完成后,發送了第四次握手的ACK包后(最后的 ACK 是由「主動關閉連接」的一端發出的),就進入了TIME_WAIT狀態。
CLOSE
當被動方收到ACK報文后,也即可以進入到CLOSED可用狀態了。
二、雙方同時調用close(),FIN_WAIT_1狀態后進入CLOSING狀態
CLOSING狀態
CLOSING: 當發送FIN報文后進入fin_wait_1狀態,過后并沒有收到對方的ACK報文,反而也收到了對方的FIN報文。就是雙方幾乎在同時close一個SOCKET,然后就出現了雙方同時發送FIN報文的情況,表示雙方都正在關閉SOCKET連接。
closing狀態收到ack報文以后即直接進入time_wait狀態
三、TIME_WAIT狀態詳解
(1) TIME_WAIT狀態下的2MSL是什么
MSL (報文最大生存時間)
任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。(IP 報文)
RFC 793中規定MSL為2分鐘,實際應用中常用的是30秒,1分鐘和2分鐘等。
為什么TIME__WAIT狀態必須停留兩倍MSL時間
等待2MSL時間主要目的是怕最后一個 ACK包對方沒收到,那么對方在超時后將重發第三次握手的FIN包,主動關閉端接到重發的FIN包后,可以再發一個ACK應答包。
當連接處于2MSL等待階段時,任何遲到的報文段都將被丟棄。
(2) TIME_WAIT存在的意義
可靠的實現 TCP 全雙工連接的終止:
四次揮手關閉 TCP 連接過程中,最后的 ACK 是由「主動關閉連接」的一端發出的,如果這個 ACK 丟失,則,對方會重發 FIN 請求,因此,在「主動關閉連接」的一段,需要維護一個 time_wait 狀態,處理對方重發的 FIN 請求;
假設發起主動關閉方發送的ACK (4次交互的最后一個包)在網絡中丟失,那么由于TCP的重傳機制,被動關閉方需要重發FIN(第三次揮手),在該FIN到達主動發起方之前,client必須維護這條連接的狀態(盡管它已調用過close),具體而言,就是這條TCP連接對應的(源IP,源端口)資源不能被立即釋放或重新分配。直到對端重發的FIN達到,client也重發ACK后,該TCP連接才能恢復初始的CLOSED狀態。如果主動方不進入TIME_WAIT以維護其連接狀態,則當被動方重發的FIN達到時,主動方的TCP傳輸層會以RST包響應對方,這會被對方認為有錯誤發生(而事實上,這是正常的關閉連接過程,并非異常)。
處理延遲到達的報文:
由于路由器可能抖動,TCP 報文會延遲到達,為了避免「延遲到達的 TCP 報文」被誤認為是「新 TCP 連接」的數據,則,需要在允許新創建 TCP 連接之前,保持一個不可用的狀態,等待所有延遲報文的消失,一般設置為 2 倍的 MSL(報文的最大生存時間),解決「延遲達到的 TCP 報文」問題。
TCP是流式的,所有包到達的順序是不一致的,依靠序列號由TCP協議棧做順序的拼接;假設如果一個新連接建立后舊連接的seq=1000包到達對端,可能會頂替掉新連接的seq=1000,造成錯誤
(3) 為什么會出現大量的TIME_WAIT狀態
大量的短連接存在
TCP 四次揮手關閉連接機制中,為了保證 ACK 重發和丟棄延遲數據,設置 time_wait 為 2 倍的 MSL(報文最大存活時間)
(4) TIME_WAIT狀態的影響
time_wait 狀態下,TCP 連接占用的端口,無法被再次使用, 要等到2MSL時間結束,才可繼續使用,而TCP 端口數量上限是 6.5w(65535,16 bit)
大量 time_wait 狀態存在,會導致新建 TCP 連接會出錯,address already in use : connect 異常
解決方法
- 服務器端允許 time_wait 狀態的 socket 被重用
- 縮減 time_wait 時間,設置為 1 MSL(即,2 mins)
四、HTTP中一般服務端是請求斷開方
在HTTP1.1協議中,有個 Connection 頭,Connection有兩個值,close和keep-alive,這個頭就相當于客戶端告訴服務端,服務端你執行完成請求之后,是關閉連接還是保持連接,保持連接就意味著在保持連接期間,只能由客戶端主動斷開連接。還有一個keep-alive的頭,設置的值就代表了服務端保持連接保持多久。
HTTP默認的Connection值為close,那么就意味著關閉請求的一方幾乎都會是由服務端這邊發起的。那么這個服務端產生TIME_WAIT過多的情況就很正常了。
雖然HTTP默認Connection值為close,但是,現在的瀏覽器發送請求的時候一般都會設置Connection為keep-alive了。所以,也有人說,現在沒有必要通過調整參數來使TIME_WAIT降低了。