TCP連接狀態CLOSE_WAIT和TIME_WAIT詳細分析


一、TCP連接狀態簡介

TCP協議規定,對於已經建立的連接,網絡雙方要進行四次揮手才能成功斷開連接,如果缺少了其中某個步驟,將會使連接處於假死狀態,連接本身占用的資源不會被釋放。

網絡服務器程序要同時管理大量連接,所以很有必要保證無用連接完全斷開,否則大量僵死的連接會浪費許多服務器資源。在眾多TCP狀態中,最值得注意的狀態有兩個:CLOSE_WAIT和TIME_WAIT。

如果服務器出現異常,百分之八九十都是下面兩種情況:

  • 服務器保持了大量TIME_WAIT狀態;
  • 服務器保持了大量CLOSE_WAIT狀態;

因為linux分配給一個用戶的文件句柄是有限的,而TIME_WAIT和CLOSE_WAIT兩種狀態如果一直被保持,那么意味着對應數目的通道就一直被占着,而且是“占着茅坑不使勁”,一旦達到句柄數上限,新的請求就無法被處理了,接着就是大量Too Many Open Files異常,tomcat崩潰。

在服務器的日常維護過程中,會經常用到下面的命令:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

 

TCP連接狀態CLOSE_WAIT和TIME_WAIT詳細分析

 

它會顯示例如下面的信息:

TIME_WAIT 814

CLOSE_WAIT 1

FIN_WAIT1 1

ESTABLISHED 634

SYN_RECV 2

LAST_ACK 1

 

常用的三個狀態是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主動關閉,CLOSE_WAIT 表示被動關閉。

二、TCP連接狀態詳解

具體每種狀態什么意思,可以看看下面的介紹。

TCP連接狀態CLOSE_WAIT和TIME_WAIT詳細分析

 

1、LISTENING狀態

FTP服務啟動后首先處於偵聽(LISTENING)狀態。

2、ESTABLISHED狀態

ESTABLISHED的意思是建立連接。表示兩台機器正在通信。

3、CLOSE_WAIT

對方主動關閉連接或者網絡異常導致連接中斷,這時我方的狀態會變成CLOSE_WAIT,此時我方要調用close()來使得連接正確關閉。

4、TIME_WAIT

我方主動調用close()斷開連接,收到對方確認后狀態變為TIME_WAIT。TCP協議規定TIME_WAIT狀態會一直持續2MSL(即兩倍的分段最大生存期),以此來確保舊的連接狀態不會對新連接產生影響。處於TIME_WAIT狀態的連接占用的資源不會被內核釋放,所以作為服務器,在可能的情況下,盡量不要主動斷開連接,以減少TIME_WAIT狀態造成的資源浪費。

目前有一種避免TIME_WAIT資源浪費的方法,就是關閉socket的LINGER選項。但這種做法是TCP協議不推薦使用的,在某些情況下這個操作可能會帶來錯誤。

5、SYN_SENT狀態

SYN_SENT狀態表示請求連接,當你要訪問其它的計算機的服務時首先要發個同步信號給該端口,此時狀態為SYN_SENT,如果連接成功了就變為 ESTABLISHED,此時SYN_SENT狀態非常短暫。但如果發現SYN_SENT非常多且在向不同的機器發出,那你的機器可能中了沖擊波或震盪波 之類的病毒了。這類病毒為了感染別的計算機,它就要掃描別的計算機,在掃描的過程中對每個要掃描的計算機都要發出了同步請求,這也是出現許多 SYN_SENT的原因。

三、TCP連接的釋放(四次揮手)

數據傳輸完畢后,雙方都可釋放連接。最開始的時候,客戶端和服務器都是處於ESTABLISHED狀態,然后客戶端主動關閉,服務器被動關閉。

TCP連接狀態CLOSE_WAIT和TIME_WAIT詳細分析

 

1. 客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號為seq=u(等於前面已經傳送過來的數據的最后一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。

2. 服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。

3. 客戶端收到服務器的確認請求后,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最后的數據)。

4. 服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,此時,服務器就進入了LAST-ACK(最后確認)狀態,等待客戶端的確認。

5. 客戶端收到服務器的連接釋放報文后,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2*MSL(最長報文段壽命,max segment lifetime)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態。

6. 服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。

四、服務器保持了大量TIME_WAIT狀態

這種情況比較常見,一些爬蟲服務器或者WEB服務器(如果網管在安裝的時候沒有做內核參數優化的話)上經常會遇到這個問題,這個問題是怎么產生的呢?

從上面的示意圖可以看得出來,TIME_WAIT是主動關閉連接的一方保持的狀態,對於爬蟲服務器來說它本身就是客戶端,在完成一個爬取任務之后,它就會發起主動關閉連接,從而進入TIME_WAIT的狀態。然后在保持這個狀態2MSL(max segment lifetime)時間之后,徹底關閉回收資源。

為什么要這么做?明明就已經主動關閉連接了為啥還要保持資源一段時間呢?

這個是TCP/IP的設計者規定的,主要出於以下兩個方面的考慮:

1. 防止上一次連接中的包,迷路后重新出現,影響新連接(經過2MSL,上一次連接中所有的重復包都會消失)。

2. 可靠的關閉TCP連接。在主動關閉方發送的最后一個 ack(fin) ,有可能丟失,這時被動方會重新發fin,如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。另外這么設計TIME_WAIT 會定時的回收資源,並不會占用很大資源的,除非短時間內接受大量請求或者受到攻擊。

五、服務器保持了大量CLOSE_WAIT狀態

TIME_WAIT狀態可以通過優化服務器參數得到解決,因為發生TIME_WAIT的情況是服務器自己可控的,要么就是對方連接的異常,要么就是自己沒有迅速回收資源,總之不是由於自己程序錯誤導致的。

但是CLOSE_WAIT就不一樣了,從上面的圖可以看出來,如果一直保持在CLOSE_WAIT狀態,那么只有一種情況,就是在對方關閉連接之后服務器程序自己沒有進一步發出ack信號。換句話說,就是在對方連接關閉之后,程序里沒有檢測到,或者程序壓根就忘記了這個時候需要關閉連接,於是這個資源就一直被程序占着。

執行如下命令:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

 

發現CLOSE_WAIT的數量始終在400以上,一直沒降過。

個人覺得這種情況,通過服務器內核參數也沒辦法解決,服務器對於程序搶占的資源沒有主動回收的權利,除非終止程序運行。

如果你使用的是HttpClient並且你遇到了大量CLOSE_WAIT的情況,那么這篇日志也許對你有用:

HttpClient連接池拋出大量
ConnectionPoolTimeoutException: Timeout waiting for connection異常排查

http://blog.csdn.net/shootyou/article/details/6615051

在那邊日志里頭我舉了個場景,來說明CLOSE_WAIT和TIME_WAIT的區別,這里重新描述一下:

服 務器A是一台爬蟲服務器,它使用簡單的HttpClient去請求資源服務器B上面的apache獲取文件資源,正常情況下,如果請求成功,那么在抓取完資源后,服務器A會主動發出關閉連接的請求,這個時候就是主動關閉連接,服務器A的連接狀態我們可以看到是TIME_WAIT。

如果一旦發生異常呢?假設請求的資源服務器B上並不存在,那么這個時候就會由服務器B發出關閉連接的請求,服務器A就是被動的關閉了連接,如果服務器A被動關閉連接之后程序員忘了讓HttpClient釋放連接,那就會造成CLOSE_WAIT的狀態了。

所以如果將大量CLOSE_WAIT的解決辦法總結為一句話那就是:查代碼。因為問題出在服務器程序里頭。

轉自https://www.toutiao.com/i6840971427747725827/?timestamp=1592959878&app=news_article&group_id=6840971427747725827&use_new_style=0&req_id=202006240851180100080431041675E71D

喜歡這篇文章?歡迎打賞~~

 


免責聲明!

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



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