UNIX網絡編程--TIME_WAIT

先看以下TIME_WAIT狀態是怎麼產生的,最好的解釋就是看TCP狀態轉換圖咯


對與每個狀態的解釋如下

1、建立連接協議(三次握手)
(1)客戶端發送一個TCP segment(Seq 由主機產生,假設爲x, SYN=1)到服務器。這是三次握手過程中的segment 1。

(2) 服務器端迴應客戶端的,這是三次握手中的第2次握手,此時發送2個segment,第一個segment中SYN=1, Seq爲主機產生,假設爲y;第二個segment中ACK=1,Seq爲客戶端發送的Seq+1即x+1,這個表示它對剛纔客戶端SYN segment的迴應;同時又標誌SYN給客戶端,詢問客戶端是否準備好進行數據通訊。

(3) 客戶必須再次迴應服務段一個ACK segment,此時該segment 的Seq爲服務器所發的確認segment的Seq+1,即y+1。

2、連接終止協議(四次握手)
由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

(1) TCP客戶端發送一個帶FIN標誌的segment,假設Seq爲x,用來關閉客戶到服務器的數據傳送。

  (2)   服務器收到這個帶FIN標誌的segment後,它發回一個帶ACK標誌的segment,此時Seq爲x+1,表示對客戶端請求關閉連接的迴應

  (3) 服務器關閉客戶端的連接,發送一個帶FIN標誌的segment,假設Seq爲y給客戶端

(4) 客戶段發回帶ACK標誌segment表示確認,纔是segment的Seq爲y+1,表示對服務器segment的確認

下圖爲TCP連接與終止的序列圖


CLOSED: 這個沒什麼好說的了,表示初始狀態。

LISTEN: 這個也是非常容易理解的一個狀態,表示服務器端的某個SOCKET處於監聽狀態,可以接受連接了。

SYN_RCVD: 這個狀態表示接受到了SYN報文,在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一箇中間狀態,很短暫,基本上用netstat你是很難看到這種狀態的,除非你特意寫了一個客戶端測試程序,故意將三次TCP握手過程中最後一個ACK報文不予發送。因此這種狀態時,當收到客戶端的ACK報文後,它會進入到ESTABLISHED狀態。

SYN_SENT: 這個狀態與SYN_RCVD遙想呼應,當客戶端SOCKET執行CONNECT連接時,它首先發送SYN報文,因此也隨即它會進入到了SYN_SENT狀態,並等待服務端的發送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已發送SYN報文。

ESTABLISHED:這個容易理解了,表示連接已經建立了。

FIN_WAIT_1: 這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。

FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連接,也即有一方要求close連接,但另外還告訴對方,我暫時還有點數據需要傳送給你,稍後再關閉連接。

TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。

注:MSL(最大分段生存期)指明TCP報文在Internet上最長生存時間,每個具體的TCP實現都必須選擇一個確定的MSL值.RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒.TIME_WAIT 狀態最大保持時間是2 * MSL,也就是1-4分鐘.

CLOSING: 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你發送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方幾乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。

CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給自己,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有數據發送給對方,如果沒有的話,那麼你也就可以close這個SOCKET,發送FIN報文給對方,也即關閉連接。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連接。

LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。


現在開始詳細解釋下TIME_WAIT狀態(注意,該狀態只有主動發起關閉的一方纔能進入)

 首先爲什麼需要TIME_WAIT狀態。理由如下

 1. 該狀態可以可靠的實現TCP全雙工連接的終止

 2. 有了該狀態,就允許了老的重複分節在網絡中消逝

第一個理由的解釋:假設最終的ACK丟失,服務器將重發FIN,因而客戶端必須維護此信息而重發ACK。要是不維護該狀態信息的話,客戶端將發送帶RST標誌的TCP segment,而服務器將此解釋成一個錯誤,這樣就沒有正確的徹底的終止T某個連接上兩個方向的數據流(全雙工關閉)

第二個理由的解釋:首先解釋下什麼是迷途分組:如果某個路由其出現故障或崩潰,路由協議需化一段時間才能未定找出一條旋路通路,而在這段時間內有可能出現路由循環。假設該迷途的分組爲一個TCP 分節,在它迷途期間,發端TCP會超時重傳該分組,該分組最終也到達收端,而此時路由循環回覆,之前迷途的分組也被送到收到,此時就出現了兩個帶FIN標誌的segment,一個爲超時重傳segment,一個迷途segment,因此TCP必須正確處理這些重複的segment,而TIME_WAIT就能很好的解決該問題。如果有了TIME_WAIT狀態,TCP將不會接收該迷途segment,而TIME_WAIT狀態持續時間爲2MSL,這就足夠運行某個方向上的segment最多存活MSL秒即被丟棄,,另一個方向的應答最多存活MSL秒也被丟棄。這樣就能保證老的重複的segmeng在網絡中消逝


那如果當前系統,TIME_WAIT過多咋解決呢?下面爲解決方法

[root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_tw_recycle = 0
[root@aaa1 ~]#

vi /etc/sysctl
增加或修改net.ipv4.tcp_tw值:
net.ipv4.tcp_tw_reuse = 1   //打開SO_RESUEADDR選項
net.ipv4.tcp_tw_recycle = 1   //recyse是加速TIME-WAIT sockets回收

使內核參數生效:
[root@aaa1 ~]# sysctl -p

[root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

用netstat再觀察正常


對於SO_RESUEADDR套接字選項的解釋如下

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

參考博文地址:

http://www.cszhi.com/20120425/tcp-time_wait.html

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