tcp關閉狀態詳解
1. tcp關閉連線狀態轉換
上圖是tcp連線主動關閉端的狀態轉換圖:
(1)應用層呼叫close函式發起關閉連線請求
(2)傳送FIN到對端,關閉寫通道,自己進入FIN_WAIT1狀態
(3)等待對端的確認ACK到來,接受到ACK後進入FIN_WAIT2狀態;如果在超時時間內沒有收到確認ACK直接進入CLOSED狀態
(4)如果在FIN_WAIT1狀態時收到了對端的FIN則進入CLOSING狀態(雙發都發出了關閉連線請求)
(5)在FIN_WAIT2接受到了對端FIN後進入TIME_WAIT狀態;如果在超時時間內沒有收這個FIN則直接進入CLOSED狀態
(6)在TIME_WAIT狀態等待2個MSL(2個報文最長存活週期)後進入CLOSED狀態
上圖是tcp連線被動關閉方的狀態轉換圖
(1)收到對端FIN後,關閉讀通道進入CLOSE_WAIT狀態
(2)在CLOSE_WAIT狀態等待應用層呼叫close函式關閉連線
(3)如果在超時時間內呼叫了close,則進入LAST_ACK狀態;否則直接進入CLOSED狀態
(4)在LAST_ACK狀態,傳送FIN到對端並等待對端的確認ACK
(5)如果在超時時間內收到了確認ACK則進入CLOSED狀態,否則直接進入CLOSED狀態
2. 狀態分析
2.1 FIN_WAIT1
這個狀態在實際的工作中很少見。主動方呼叫close函式關閉連線後立刻進入FIN_WAIT1狀態,此時只要收到對端確認ACK後馬上會進入FIN_WAIT2狀態。因為對端確認ACK是TCP協議棧自己控制的,所以很快就會發出。出現場景:主動方等待ACK過程中網路斷掉了,導致長時間收不到ACK,主動方就會停留在CLOSE_WAIT1狀態上(超時時間:一般預設60s超時)。此時我們可以使用netstat -anpt 命令看到這種狀態。
NOTE:這個狀態如果超時,將直接進入CLOSED狀態。關於超時時間你可能需要了解:tcp_fin_timeout這個配置引數。
2.2 FIN_WAIT2
這個狀態比較常見。主動端在等待對端FIN到來過程中,會一你直保持這個狀態(超時時間:一般預設是60s)。由於網路中斷,或者對端很忙還沒來得及傳送FIN、或者對端有bug忘記關閉連線等都會導致主動端長時間處於FIN_WAIT2狀態。如果主動方發現大量FIN_WAIT2狀態時,應該引起相關人員的注意,這可能是網路不穩、對端程式bug的表現。
NOTE:這個狀態如果超時,將直接進入CLOSED狀態。關於超時時間你可能需要了解:tcp_fin_timeout這個配置引數。
2.3 TIME_WAIT
這個狀態最常見。主動方收到對端的FIN後進入TIME_WAIT狀態。然後傳送最後一個確認ACK到對端。之後等待2個最大的報文存活週期,正常的關閉流程客戶端TCP連線都會經過這個狀態,最終進入CLOSED狀態。所以我們使用netstat -anpt命令發現客戶端有很多的TIME_WAIT,一般這是正常的現象。
NOTE:
* 這個狀態的連線還沒有真正關閉,所以佔用的檔案控制代碼和埠號等資源也沒有釋放。如果TIME_WAIT狀態的連線過多,將會導致檔案控制代碼或者埠號等資源不足。如果你想控制這個狀態連線的數量,你可能需要詳細瞭解:tcp_max_tw_buckets tcp_tw_recycle tcp_tw_reuse三個系統配置。
* 對於有大量短連結服務的伺服器來說,服務端主動關閉連線會產生大量的TIME_WAIT,如果不及時回收這些個連線會造成資源嚴重浪費。不過一個應用層系統都有自己最大併發連線數限制、作業系統也有最大TIME_WAIT連線數限制。所以一般也不會產生大的問題。
* 等待2MSL的原因:
(1)保證殘留網路報不會被新連線接收而產生資料錯亂。由於自己上一次傳送的資料包可能還殘留在網路中,等待2MSL時間可以保證所有殘留的網路報在自己關閉前都已經超時。
(2)確保自己最後ACK發到對端。因為ACK傳送也可能會失敗,這是對端會重新發送FIN,如果已經CLOSED了那麼對端將收到RST而不是ACK了,這不符合TCP可靠關閉策略。
2.4 CLOSING
雙發幾乎同時都呼叫了close介面主動關閉連線,此時都進入了FIN_WAIT1狀態。如果在FIN_WAIT1狀態期望收到對方的ACK但卻收到了對方的FIN,這時候雙方都進入CLOSING狀態。然後都給對方一個ACK確認,收到了ACK後就會進入CLOSED狀態了。
NOTE:這個狀態如果超時,將直接進入CLOSED狀態。關於這個超時時間的設定我暫時還沒有找到?
2.5 CLOSE_WAIT(重點說明下這個狀態)
這個狀態表明TCP連線等待被關閉。只可能在被動方出現。如果被動方存在大量的CLOSE_WAIT狀態需要因為我們的特別注意了。我們要仔細研究確認為什麼被動方遲遲不願關閉連線(或許是我們程式中的bug開啟了連線,用完後卻忘記關閉)
目前開發過程中遇到如下這個場景導致被動方有很多的CLOSE_WAIT狀態:
A是一個應用程式,B是一個tomcat伺服器
A開了一個連線Conn,傳送請求給B
A接受相應資料後沒有呼叫Conn.close關閉連線,在A端垃圾回收這些Conn物件前,這些連線一直保持著
B端的連線超時後會主動發起關閉連線請求給A,此時A進入了CLOSE_WAIT狀態,B進入了FIN_WAIT2狀態,由於A遲遲不傳送FIN給B,B端觸發timeout直接進入了CLOSED狀態。
這樣一個場景B端由於有超時設定一個為60s,不會存在大量的FIN_WAIT2狀態
但是A端就會殘留大量的CLOSE_WAIT狀態(CLOSE_WAIT狀態也有超時,但是太大,預設為43200s,詳情見tcp_timeout_close_wait系統配置)。還好A端的java虛擬機器的最大對記憶體配置較小,由於CLOSE_WAIT狀態連線同樣佔用了記憶體資源,數量很多後就會觸發垃圾回收,此時A端的CLOSE_WAIT的連線Conn物件就會被銷燬了(同時記憶體和控制代碼、埠等資源也被釋放了)
很明顯這是一個bug導致的問題,如果沒有及時回收的話,就會把記憶體、控制代碼或者埠等資源給用完,導致程式crash掉。
2.6 LAST_ACK
這個狀態只可能在被動端出現。當被動端呼叫close介面關閉連線後便會進入這個狀態,同時傳送一個FIN給對端。在接受對端的ACK確認後便會進入CLOSED狀態,這個狀態一般不易出現,除非網路中斷,一般對端會很快給與響應的。
2.7 狀態總結
主動端可能出現的狀態:FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT
被動端可能出現的狀態:CLOSE_WAIT LAST_ACK
敘述中提到的超時時間在我的另一片文章tcp連線超時那點事中有具體的分析
NOTE:
(1)主動端出現大量的FIN_WAIT1時需要注意網路是否暢通、出現大量的FIN_WAIT2需要仔細檢查程式為何遲遲收不到對端的FIN(可能是主動方或者被動方的bug)、出現大量的TIME_WAIT需要注意系統的併發量/socket控制代碼資源/記憶體使用/埠號資源等。
(2)被動端出現大量的 CLOSE_WAIT 需要仔細檢查為何自己遲遲不願呼叫close關閉連線(可能是bug,socket開啟用完沒有關閉)
轉載於:https://my.oschina.net/xlplbo/blog/337107