TIME_WAIT狀態

1. TIME_WAIT狀態

主動關閉方在收到被動關閉方的FIN包後並返回ACK後,會進入TIME_WAIT狀態,TIME_WAIT狀態又稱2MSL狀態,每一個TCP鏈接都必須有一個最大報文段生存時間MSL,在網絡傳輸中超過這個時間的報文段將被丟棄。當TCP鏈接發起一個主動關閉,併發出最後一個ACK時,必須在TIME_WAIT狀態停留兩倍MSL時間,在2MSL等待期間,定義這個鏈接的插口(客戶端IP地址和端口號,服務器IP地址和端口號的四元組)將不能再被使用。2MSL狀態存在有兩個理由:html

  • 1.容許老的重複報文分組在網絡中消逝。java

  • 2.保證TCP全雙工鏈接的正確關閉。linux

第一個理由是假如咱們在192.168.1.1:500039.106.170.184:6000創建一個TCP鏈接,一段時間後咱們關閉這個鏈接,再基於相同插口創建一個新的TCP鏈接,這個新的鏈接稱爲前一個鏈接的化身。老的報文頗有可能因爲某些緣由遲到了,那麼新的TCP鏈接頗有可能會將這個遲到的報文認爲是新的鏈接的報文,而致使數據錯亂。爲了防止這種狀況的發生TCP鏈接必須讓TIME_WAIT狀態持續2MSL,在此期間將不能基於這個插口創建新的化身,讓它有足夠的時間使遲到的報文段被丟棄。web

第二個理由是由於若是主動關閉方最終的ACK丟失,那麼服務器將會從新發送那個FIN,以容許主動關閉方從新發送那個ACK。要是主動關閉方不維護2MSL狀態,那麼主動關閉將會不得不響應一個RST報文段,而服務器將會把它解釋爲一個錯誤,致使TCP鏈接沒有辦法完成全雙工的關閉,而進入半關閉狀態。編程

1.1 爲何是維持2MSL

  • 一個 MSL是確保主動關閉方最後的 ACK可以到達對端。
  • 一個 MSL是確保被動關閉方重發的 FIN可以被主動關閉方收到。

2.處理TIME_WAIT狀態

一個web服務器最大的端口數量是65535個,若是這個服務器做爲客戶端不停的和服務端不停的建立短鏈接,就會致使有大量的TCP進入TIME_WAIT狀態。緩存

當服務端是主動關閉方,由於有TIME_WAIT狀態的存在,在重啓程序的時候可能會出現java.net.BindException: Address already in use的錯誤,這是由於這個端口TIME_WAIT狀態須要等待2MSL。在RFC793中規定MSL的時間爲2min,在實際使用中通常是30s或者1min,在高併發的狀況下毫無疑問,這將形成大量鏈接沒法創建的問題,那麼有什麼方法能夠處理這些問題那?服務器

2.1 SO_REUSEADDR

  • SO_REUSEADDR設置爲1在 TIME_WAIT時容許套接字端口複用。
  • SO_REUSEADDR設置爲0 TIME_WAIT時不容許容許套接字端口複用。

在Java中能夠經過如下代碼設置:微信

ServerSocket serverSocket = new ServerSocket();
// setReuseAddress 必須在 bind 函數調用以前執行
serverSocket.setReuseAddress(true);
複製代碼

SO_REUSEADDR能夠用在如下四種狀況下。(摘自《Unix網絡編程》卷一,即UNPv1)網絡

  1. 當有一個有相同本地地址和端口的socket1處於TIME_WAIT狀態時,而你啓動的程序的socket2要佔用該地址和端口,你的程序就要用到該選項。
  2. SO_REUSEADDR容許同一port上啓動同一服務器的多個實例(多個進程)。但每一個實例綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可 以測試這種狀況。
  3. SO_REUSEADDR容許單個進程綁定相同的端口到多個 socket上,但每一個 socket綁定的ip地址不一樣。這和2很類似,區別請看 UNPv1
  4. SO_REUSEADDR容許徹底相同的地址和端口的重複綁定。但這隻用於UDP的多播,不用於TCP。

因此SO_REUSEADDR並非在全部狀況下均可以使用的。併發

參考:TCP套接字端口複用SO_REUSEADDR

2.2 TCP頭部時間戳選項(TCP Timestamps Option)

時間戳可選項主要包含4個部分:

  1. kind:類型
  2. length:長度
  3. TimeStamp value:發送方時間戳
  4. TimeStamp echo reply:回顯時間戳

時間戳可選項能夠處理序號迴繞,判斷亂序,更加準確的計算RTT,在linux中能夠經過net.ipv4.tcp_timestamps設置,通常默認是1(打開)。

發送方在發送數據時將發送數據的時間X放到發送方時間戳TSval中。

接收方在接收到數據的時候將收到的時間X原封不動的放到回顯時間戳TSecr中,同時將本身發送數據的時間Z放到發送方時間戳TSval中,以此類推。

2.3 net.ipv4.tcp_tw_reuse

在linux中tcp_tw_reuse默認爲0,設置爲1後表示客戶端容許TIME_WAIT狀態重用。重用的條件是:

  1. tcp_tw_reuse選項和 tcp_timestamps也必須同時打開;
  2. 開啓後收到最後一個包後超過1s。

當開啓tcp_tw_reuse後,主動關閉方收到包的時間戳比存儲的時間戳小,則證實收到的包是一箇舊的鏈接的包,直接丟棄。

2.4 net.ipv4.tcp_tw_recycel

在linux中tcp_tw_recycel默認爲0,設置爲1後開啓(tcp_timestamps必須同時打開),開啓tcp_tw_recycel後服務器將會緩存每一個套接字的最新時間戳。對於新的鏈接,若是SYN包中的時間戳小於以前緩存的套接字的時間戳,則直接丟棄,不然容許複用TIME_WAIT

這種機制在通過NAT或者負載均衡後,會發生嚴重的問題。由於不一樣請求通過NAT或者負載均衡後IP多是同樣的,就會致使TIME_WAIT沒法複用。因此不推薦打開這個參數。

關注做者微信公衆號:

相關文章
相關標籤/搜索