主動關閉方在收到被動關閉方的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:5000
和39.106.170.184:6000
創建一個TCP鏈接,一段時間後咱們關閉這個鏈接,再基於相同插口創建一個新的TCP鏈接,這個新的鏈接稱爲前一個鏈接的化身。老的報文頗有可能因爲某些緣由遲到了,那麼新的TCP鏈接頗有可能會將這個遲到的報文認爲是新的鏈接的報文,而致使數據錯亂。爲了防止這種狀況的發生TCP鏈接必須讓TIME_WAIT狀態持續2MSL
,在此期間將不能基於這個插口創建新的化身,讓它有足夠的時間使遲到的報文段被丟棄。web
第二個理由是由於若是主動關閉方最終的ACK
丟失,那麼服務器將會從新發送那個FIN
,以容許主動關閉方從新發送那個ACK
。要是主動關閉方不維護2MSL
狀態,那麼主動關閉將會不得不響應一個RST
報文段,而服務器將會把它解釋爲一個錯誤,致使TCP鏈接沒有辦法完成全雙工的關閉,而進入半關閉狀態。編程
MSL
是確保主動關閉方最後的
ACK
可以到達對端。
MSL
是確保被動關閉方重發的
FIN
可以被主動關閉方收到。
一個web服務器最大的端口數量是65535個,若是這個服務器做爲客戶端不停的和服務端不停的建立短鏈接,就會致使有大量的TCP進入TIME_WAIT
狀態。緩存
當服務端是主動關閉方,由於有TIME_WAIT
狀態的存在,在重啓程序的時候可能會出現java.net.BindException: Address already in use
的錯誤,這是由於這個端口TIME_WAIT
狀態須要等待2MSL
。在RFC793
中規定MSL
的時間爲2min,在實際使用中通常是30s或者1min,在高併發的狀況下毫無疑問,這將形成大量鏈接沒法創建的問題,那麼有什麼方法能夠處理這些問題那?服務器
SO_REUSEADDR
設置爲1在
TIME_WAIT
時容許套接字端口複用。
SO_REUSEADDR
設置爲0
TIME_WAIT
時不容許容許套接字端口複用。
在Java中能夠經過如下代碼設置:微信
ServerSocket serverSocket = new ServerSocket();
// setReuseAddress 必須在 bind 函數調用以前執行
serverSocket.setReuseAddress(true);
複製代碼
SO_REUSEADDR
能夠用在如下四種狀況下。(摘自《Unix網絡編程》卷一,即UNPv1)網絡
當有一個有相同本地地址和端口的socket1處於TIME_WAIT狀態時,而你啓動的程序的socket2要佔用該地址和端口,你的程序就要用到該選項。 SO_REUSEADDR
容許同一port上啓動同一服務器的多個實例(多個進程)。但每一個實例綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可 以測試這種狀況。SO_REUSEADDR
容許單個進程綁定相同的端口到多個socket
上,但每一個socket
綁定的ip地址不一樣。這和2很類似,區別請看UNPv1
。SO_REUSEADDR
容許徹底相同的地址和端口的重複綁定。但這隻用於UDP的多播,不用於TCP。
因此SO_REUSEADDR
並非在全部狀況下均可以使用的。併發
時間戳可選項主要包含4個部分:
時間戳可選項能夠處理序號迴繞,判斷亂序,更加準確的計算RTT
,在linux中能夠經過net.ipv4.tcp_timestamps
設置,通常默認是1
(打開)。
發送方在發送數據時將發送數據的時間X
放到發送方時間戳TSval
中。
接收方在接收到數據的時候將收到的時間X
原封不動的放到回顯時間戳TSecr
中,同時將本身發送數據的時間Z
放到發送方時間戳TSval
中,以此類推。
在linux中tcp_tw_reuse
默認爲0,設置爲1
後表示客戶端容許TIME_WAIT
狀態重用。重用的條件是:
tcp_tw_reuse
選項和
tcp_timestamps
也必須同時打開;
當開啓tcp_tw_reuse
後,主動關閉方收到包的時間戳比存儲的時間戳小,則證實收到的包是一箇舊的鏈接的包,直接丟棄。
在linux中tcp_tw_recycel
默認爲0,設置爲1後開啓(tcp_timestamps
必須同時打開),開啓tcp_tw_recycel
後服務器將會緩存每一個套接字的最新時間戳。對於新的鏈接,若是SYN
包中的時間戳小於以前緩存的套接字的時間戳,則直接丟棄,不然容許複用TIME_WAIT
。
這種機制在通過NAT
或者負載均衡後,會發生嚴重的問題。由於不一樣請求通過NAT或者負載均衡後IP多是同樣的,就會致使TIME_WAIT
沒法複用。因此不推薦打開這個參數。
關注做者微信公衆號: