本節書摘來自異步社群《unix網絡程式設計 卷1:套接字聯網api(第3版)》一書中的第2章,第2.7節,作者:【美】w. richard stevens , bill fenner , andrew m. rudoff著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視
毫無疑問,tcp中有關網絡程式設計最不容易了解的是它的time_wait狀态。在圖2-4中我們看到執行主動關閉的那端經曆了這個狀态。該端點停留在這個狀态的持續時間是最長分節生命期(maximum segment lifetime,msl)的兩倍,有時候稱之為2msl。
任何tcp實作都必須為msl選擇一個值。rfc 1122[braden 1989]的建議值是2分鐘,不過源自berkeley的實作傳統上改用30秒這個值。這意味着time_wait狀态的持續時間在1分鐘到4分鐘之間。msl是任何ip資料報能夠在網際網路中存活的最長時間。我們知道這個時間是有限的,因為每個資料報含有一個稱為跳限(hop limit)的8位字段(見圖a-1中ipv4的ttl字段和圖a-2中ipv6的跳限字段),它的最大值為255。盡管這是一個跳數限制而不是真正的時間限制,我們仍然假設:具有最大跳限(255)的分組在網絡中存在的時間不可能超過msl秒。
分組在網絡中“迷途”通常是路由異常的結果。某個路由器崩潰或某兩個路由器之間的某個鍊路斷開時,路由協定需花數秒鐘到數分鐘的時間才能穩定并找出另一條通路。在這段時間内有可能發生路由循環(路由器a把分組發送給路由器b,而b再把它們發送回a),我們關心的分組可能就此陷入這樣的循環。假設迷途的分組是一個tcp分節,在它迷途期間,發送端tcp逾時并重傳該分組,而重傳的分組卻通過某條候選路徑到達最終目的地。然而不久後(自迷途的分組開始其旅程起最多msl秒以内)路由循環修複,早先迷失在這個循環中的分組最終也被送到目的地。這個原來的分組稱為迷途的重複分組(lost duplicate)或漫遊的重複分組(wandering duplicate)。tcp必須正确處理這些重複的分組。
time_wait狀态有兩個存在的理由:
(1)可靠地實作tcp全雙工連接配接的終止;
(2)允許老的重複分節在網絡中消逝。
第一個理由可以通過檢視圖2-5并假設最終的ack丢失了來解釋。伺服器将重新發送它的最終那個fin,是以客戶必須維護狀态資訊,以允許它重新發送最終那個ack。要是客戶不維護狀态資訊,它将響應以一個rst(另外一種類型的tcp分節),該分節将被伺服器解釋成一個錯誤。如果tcp打算執行所有必要的工作以徹底終止某個連接配接上兩個方向的資料流(即全雙工關閉),那麼它必須正确處理連接配接終止序列4個分節中任何一個分節丢失的情況。本例子也說明了為什麼執行主動關閉的那一端是處于time_wait狀态的那一端:因為可能不得不重傳最終那個ack的就是那一端。
為了解存在time_wait狀态的第二個理由,我們假設在12.106.32.254的1500端口和206.168.112.219的21端口之間有一個tcp連接配接。我們關閉這個連接配接,過一段時間後在相同的ip位址和端口之間建立另一個連接配接。後一個連接配接稱為前一個連接配接的化身(incarnation),因為它們的ip位址和端口号都相同。tcp必須防止來自某個連接配接的老的重複分組在該連接配接已終止後再現,進而被誤解成屬于同一連接配接的某個新的化身。為做到這一點,tcp将不給處于time_wait狀态的連接配接發起新的化身。既然time_wait狀态的持續時間是msl的2倍,這就足以讓某個方向上的分組最多存活msl秒即被丢棄,另一個方向上的應答最多存活msl秒也被丢棄。通過實施這個規則,我們就能保證每成功建立一個tcp連接配接時,來自該連接配接先前化身的老的重複分組都已在網絡中消逝了。
這個規則存在一個例外:如果到達的syn的序列号大于前一化身的結束序列号,源自berkeley的實作将給目前處于time_wait狀态的連接配接啟動新的化身。tcpv2第958~959頁對這種情況有詳細的叙述。它要求伺服器執行主動關閉,因為接收下一個syn的那一端必須處于time_wait狀态。rsh指令具備這種能力。rfc 1185[jacobson, braden, and zhang 1990]講述了有關這種情形的一些陷阱。