關閉socket鏈接過程中的TCP狀態:TIME_WAIT狀態(開啓地址重用)

關閉鏈接過程中的TCP狀態和SOCKET處理,及可能出現的問題:

1、 TIME_WAIT

        TIME_WAIT 是主動關閉 TCP 連接的那一方出現的狀態,系統會在 TIME_WAIT 狀態下等待 2MSL(maximum segment lifetime  )後才能釋放連接(端口)。通常約合 4 分鐘以內。

      TIME_WAIT 狀態等待 2MSL(MSL(最大分段生存期)指明TCP報文在Internet上最長生存時間,每個具體的TCP實現都必須選擇一個確定的MSL值) 的意義:

      a、確保連接可靠地關閉; 即防止最後一個ACK丟失

      b避免產生套接字混淆(同一個端口對應多個套接字)

      爲什麼說可以用來避免套接字混淆呢?

      一方close發送了關閉鏈接請求,對方的應答遲遲到不了(例如網絡原因),導致TIME_WAIT超時,此時這個端口又可用了,我們在這個端口上又建立了另外一個socket鏈接。 如果此時對方的應答到了,怎麼處理呢?其實這個在TCP層已經處理了,由於有TCP序列號,所以內核TCP層,就會將包丟掉,並給對方發包,讓對方將sockfd關閉。所以應用層是沒有關係的。即我們用socket API編寫程序,就不用處理。

     注意::

         TIME_WAIT是指操作系統的定時器會等2MSL而主動關閉sockfd的一方,並不會阻塞(但是如果開啓tcp的SO_LINGER標誌,要在close時將buf中的數據發送完畢,那麼close可能會阻塞)。

         當主動方關閉sockfd後,對方可能不知道這個事件。那麼當對方(被動方)寫數據,即send時,將會產生錯誤,即errno爲: ECONNRESET。

         服務器產生大量 TIME_WAIT 的原因(一般我們不這樣開發Server,但是web服務器等這種多客戶端的Server,是需要在完成一次請求後,主動關閉連接的,否則可能因爲句柄不夠用,而造成無法提供服務。)

服務器存在大量的主動關閉操作,需關注程序何時會執行主動關閉(如批量清理長期空閒的套接字等操作)。

        一般我們自己寫的服務器進行主動斷開連接的不多,除非做了空閒超時之類的管理。(TCP短鏈接是指,客戶端發送請求給服務器,客戶端收到服務器端的響應後,關閉鏈接)。


問題一: 如何避免等待60秒之後才能重啓服務 

方法:使用setsockopt,比如 

-------------------------------------------------------------------------- 
int option = 1; 

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof( option ) ) < 0 ) 

      die( "setsockopt" ); 

-------------------------------------------------------------------------- 

這個作用類似:

#讓TIME_WAIT狀態可以重用,這樣即使TIME_WAIT佔滿了所有端口,也不會拒絕新的請求造成障礙
echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
#讓TIME_WAIT儘快回收,我也不知是多久,觀察大概是一秒鐘
echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

本地可以使用的端口有:
cat /proc/sys/net/ipv4/ip_local_port_range
用這條命令會返回兩個數字,默認是:32768 61000,說明這臺機器本地能向外連接61000-32768=28232個連接,注意是本地向外連接,不是這臺機器的所有連接。
本機上listen監聽的端口儘量設置在ip_local_port_range之外,否則可能因爲loop connect而發生錯誤。

問題二: 編寫 TCP/SOCK_STREAM 服務程序時,SO_REUSEADDR到底什麼意思? 

解釋: 這個套接字選項通知內核,如果端口忙,但TCP狀態位於 TIME_WAIT ,可以重用端口。如果端口忙,而TCP狀態位於其他狀態,重用端口時依舊得到一個錯誤信息, 指明"地址已經使用中"。如果你的服務程序停止後想立即重啓,而新套接字依舊使用同一端口,此時 SO_REUSEADDR 選項非常有用。必須意識到,此時任何非期望數據到達,都可能導致服務程序反應混亂,不過這只是一種可能(重用端口,但主動關閉放的最後一個ACK丟失,被動關閉放重傳FIN包,可能影響新建立的連接)。 
一個套接字由相關五元組構成,協議、本地地址、本地端口、遠程地址、遠程端口。SO_REUSEADDR 僅僅表示可以重用本地地址、本地端口,整個相關五元組還是唯一確定的。所以,重啓後的服務程序有可能收到非期望數據。必須慎重使用 SO_REUSEADDR 選項。 



問題三: 在客戶機/服務器編程中(TCP/SOCK_STREAM),如何理解TCP自動機 TIME_WAIT 狀態? 
解釋:下面我來解釋一下 TIME_WAIT 狀態,MSL(最大分段生存期)指明TCP報文在Internet上最長生存時間,每個具體的TCP實現都必須選擇一個確定的MSL值。RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒。 

TIME_WAIT 狀態最大保持時間是2 * MSL,也就是1-4分鐘。 

IP頭部有一個TTL,最大值255。儘管TTL的單位不是秒(根本和時間無關),我們仍需假設,TTL爲255的TCP報文在Internet上生存時間不能超過MSL。 

TCP報文在傳送過程中可能因爲路由故障被迫緩衝延遲、選擇非最優路徑等等,結果發送方TCP機制開始超時重傳。前一個TCP報文可以稱爲"漫遊TCP重複報文",後一個TCP報文可以稱爲"超時重傳TCP重複報文",作爲面向連接的可靠協議,TCP實現必須正確處理這種重複報文,因爲二者可能最終都到達。 

一個通常的TCP連接終止可以用圖描述如下: 

client server 
FIN M 
close -----------------> (被動關閉) 
ACK M+1 
<----------------- 
FIN N 
<----------------- close 
ACK N+1 
-----------------> 



問題四:爲什麼需要 TIME_WAIT 狀態? 

解釋:假設最終的ACK丟失,被動關閉放將重發FIN,主動關閉放必須維護TCP狀態信息以便可以重發最終的ACK,否則會發送RST,結果server認爲發生錯誤。TCP實現必須可靠地終止連接的兩個方向(全雙工關閉),client必須進入 TIME_WAIT 狀態,因爲client可能面臨重發最終ACK的情形。 


scz 2001-08-31 13:28 

先調用close()的一方會進入TIME_WAIT狀態 


此外,考慮一種情況,TCP實現可能面臨先後兩個同樣的相關五元組。如果前一個連接處在 TIME_WAIT 狀態,而允許另一個擁有相同相關五元組的連接出現,可能處理TCP報文時,兩個連接互相干擾。使用 SO_REUSEADDR 選項就需要考慮這種情況。

 

問題五:爲什麼 TIME_WAIT 狀態需要保持 2MSL 這麼長的時間? 

如果 TIME_WAIT 狀態保持時間不足夠長(比如小於2MSL),第一個連接就正常終止了。 第二個擁有相同相關五元組的連接出現,而第一個連接的重複報文到達(最後的ACK沒有被對端收到,被動關閉放重傳FIN包),干擾了第二個連接。TCP實現必須防止某個連接的重複報文在連接終止後出現,所以讓TIME_WAIT狀態保持時間足夠長(2MSL),連接相應方向上的TCP報文要麼完全響應完畢,要麼被丟棄。建立第二個連接的時候,不會混淆。 



2、 CLOSE_WAIT

 CLOSE_WAIT 是被動關閉 TCP 連接時產生的

如果收到另一端關閉連接的請求後本地(Server端)不關閉相應套接字就會導致本地套接字進入這一狀態

(如果對方關閉了,沒有收到關閉鏈接請求,就是下面的不正常情況)

按TCP狀態機,我方收到FIN,則由TCP實現發送ACK,因此進入CLOSE_WAIT狀態。但如果我方不執行close(),就不能由CLOSE_WAIT遷移到LAST_ACK,則系統中會存在很多CLOSE_WAIT狀態的連接

如果存在大量的 CLOSE_WAIT,則說明客戶端併發量大,且服務器未能正常感知客戶端的退出,也並未及時 close 這些套接字。(如果不及時處理,將會出現沒有可用的socket描述符的問題,原因是sockfd耗盡)。

 

正常情況下::  

        一方關閉sockfd,外一方將會有讀事件產生, 當recv數據時,如果返回值爲0,表示對端已經關閉。此時我們應該調用close,將對應的sockfd也關閉掉。

不正常情況下:: 

        一方關閉sockfd,另外一方並不知道,(比如在close時,自己斷網了,對方就收不到發送的數據包)。此時,如果另外一方在對應的sockfd上寫send或讀recv數據。

recv時,將會返回0,表示鏈接已經斷開。

send時, 將會產生錯誤,errno爲ECONNRESET

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章