tcp關閉狀態詳解


 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打開用完沒有關閉)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM