TCP連接中的TIME_WAIT狀態
一、TIME_WAIT狀態的必要性
2、 服務器端收到FIN報文段,發送ACK表示確認,進入CLOSE_WAIT狀態。
3、 客戶端收到FIN的確認報文段,進入FIN_WAIT_2狀態。
4、 服務器端發送FIN報文端,進入LAST_ACK狀態。
5、 客戶端收到FIN報文端,發送FIN的ACK,同時進入TIME_WAIT狀態,啓動TIME_WAIT定時器,超時時間設爲2MSL。
6、 服務器端收到FIN的ACK,進入CLOSED狀態。
7、 客戶端在2MSL時間內沒收到對端的任何響應,TIME_WAIT超時,進入CLOSED狀態。
盡最大努力保證四次握手正常關閉
如果不考慮報文延遲、丟失,確認延遲、丟失等情況,TIME_WAIT的確沒有存在的必要。當網絡在不理想的情況下通常會有報文的丟失延遲發生,讓我們看下面的一個特例:
客戶端進入發送收到四次握手關閉的最後一個ACK後,進入TIME_WAIT同時發送ACK,如果其不停留2MSL時間,而是馬上關閉連接,銷燬連接上的資源,當發送如下情況時,將不能正常的完成四次握手關閉:
客戶端發送的ACK在網路上丟失,這樣服務器端收不到最後的ACK,重傳定時器超時,將重傳FIN到客戶端,由於客戶端關於該連接的所有資源都釋放,收到重傳的FIN後,它沒有關於這個FIN的任何信息,所以向服務器端發送一個RST報文端,服務器端收到RST後,認爲搞連接出現了異常(而非正常關閉)。
所以,在TIME_WAIT狀態下等待2MSL時間端,是爲了能夠正確處理第一個ACK(最長生存時間爲MSL)丟失的情況下,能夠收到對端重傳的FIN(最長生存時間爲MSL),然後重傳ACK。
是否只要主動關閉方在TIME_WAIT狀態下停留2MSL,四次握手關閉就一定正常完成呢?
答案是否定的?可以考慮如下的情況,
確保老的報文段在網絡中消失,不會影響新建立的連接
考慮如下的情況,主動關閉方在TIME_WAIT狀態下發送的ACK由於網絡延遲的原因沒有按時到底(但並沒有超過MSL的時間),導致被動關閉方重傳FIN,在FIN重傳後,延遲的ACK到達,被動關閉方進入CLOSED狀態,如果轉動關閉方在TIME_WAIT狀態下發送ACK後馬上進入CLOSED狀態(也就是沒有等待)2MSL時間,則上述的連接已不存在:現在考慮下面的情況,假設上述的鏈接的四元組爲(192.201.0.80:23,192.201.0.85:5555),由於連接已關閉,我們可以馬上建立一個新的連接,並且這個連接的四元組也是(192.201.0.80:23,192.201.0.85:5555),那麼當上一個連接的重傳FIN到達主動關閉方時,被新的連接所接受,這將導致新的連接被複位,很顯然,這不是我們希望看到的事情。
新的連接要建立,必須是在主動關閉方和被動關閉方都進入到CLOSED狀態之後纔有可能。所以,最有可能導致舊的報文段影響新的連接的情況是:
在TIME_WAIT狀態之前,主動關閉方發送的報文端在網絡中延遲,但是TIME_WAIT設定爲2MSL時,這些報文端必然會在網絡中消失(最大生存時間爲MSL)。被動關閉方最有可能影響新連接的報文段就是我們上面討論的情況,對方ACK延遲到達,在此之前重傳的FIN,這個報文端發送之後,TIME_WAIT的定時器超時時間肯定大於MSL,在1MSL時間內,這個FIN要麼在網絡中因爲生成時間到達而消失,要麼到達主動關閉方被這確的處理,不會影響新建立的連接。
二、 平靜時間
對於來自某個連接的較早替身的遲到報文段,2MSL等待可以防止將它解釋成爲使用相同插口對的新的連接的一部分。但這隻有在2MSL等待連接中的主機處於正常工作狀態時纔有效。
如果使用處於2MSL等待端口的主機出現故障,它會在MSL秒內重新啓動,並立即使用故障前仍出入2MSL的插口對來建立一個新的連接嗎?如果是這樣,在故障前從這個連接發出而遲到的報文段會被錯誤地當作屬於重啓後新連接的報文端,無論如何重新選擇重啓後新的連接的初始序號,都會發生這種情況。
爲了防止這種情況,RFC 793指出TCP在重啓後的MSL秒內不能建立任何連接。這就是平靜時間(quit time)。(有疑問???)
三、TIME_WAIT、LAST_ACK狀態對收到的報文段的處理
1. TIME_WAIT收到報文的處理
1)、收到SYN報文段
if (tiflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
iss = tp->rcv_nxt + TCP_ISSINCR;
tp = tcp_close(tp);
goto findpcb;
}
在TIME_WAIT狀態,如果收到SYN報文段,且新的起始序號大於連接上最後收到的序號,說明對端要求在已被關閉且正處於TIME_WAIT狀態的連接上重新連接。RFC 1122允許這種情況:調用TCP_CLOSE釋放處於TIME_WAIT狀態的原有連接的PCB和TCP控制塊。控制跳轉到findpcb,尋找監聽服務器,爲新的連接創建新的插口。
2) 、收到RST報文段
if (tiflags&TH_RST)
switch (tp->t_state) {
case TCPS_SYN_RECEIVED:
so->so_error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->so_error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
tcpstat.tcps_drops++;
tp = tcp_close(tp);
goto drop;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
tp = tcp_close(tp);
goto drop;
}
如果在TIME_WAIT狀態收到RST報文段,這釋放連接資源,跳轉到CLOSED狀態,提前終止TIME_WAIT狀態。
如果允許RST終止處於TIME_WAIT狀態的連接,那麼TIME_WAIT狀態也就沒有存在的必要。RFC 1337討論了這一點,及其他取消TIME_WAIT狀態可能的情況,建議不允許RST永久終止處於TIME_WAIT狀態的連接。
3) 收到ACK報文段
此時,連接的兩端已發送過FIN,且兩個FIN都已被確認。但如果TCP對遠端FIN的確認丟失,對端將重傳FIN(帶有ACK)。TCP丟棄報文端並重傳ACK。此外TIME_WAIT定時器必須被重置,時限等於2MSL。
4)收到FIN報文段
如果在TIME_WAIT狀態收到FIN,說明這是一個重複報文端。啓動TIME_WAIT定時器,時限等於2MSL,發送ACK。
2、LAST_ACK狀態
1)收到RST報文段
如果在TIME_WAIT狀態收到RST報文段,則釋放連接資源,跳轉到CLOSED狀態。
2)收到ACK置位的報文段
case TCPS_LAST_ACK:
if (ourfinisacked) {
tp = tcp_close(tp);
goto drop;
}
break;
如果FIN已確認,連接轉移到CLOSED狀態。tcp_close負責將這一狀態變遷,並同時釋放Interenet PCB和TCP控制塊。