歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

TIME_WAIT狀態下對接收到的數據包如何處理

正常情況下主動關閉連接的一端在連接正常終止後,會進入TIME_WAIT狀態,存在這個狀態有以下兩個原因(參考《Unix網絡編程》):

《UNIX網絡編程.卷2:進程間通信(第2版)》[PDF]下載 http://www.linuxidc.com/Linux/2013-01/77936.htm

1、保證TCP連接關閉的可靠性。如果最終發送的ACK丟失,被動關閉的一端會重傳最終的FIN包,如果執行主動關閉的一端沒有維護這個連接的狀態信息,會發送RST包響應,導致連接不正常關閉。

2、允許老的重復分組在網絡中消逝。假設在一個連接關閉後,發起建立連接的一端(客戶端)立即重用原來的端口、IP地址和服務端建立新的連接。老的連接上的分組可能在新的連接建立後到達服務端,TCP必須防止來自某個連接的老的重復分組在連接終止後再現,從而被誤解為同一個連接的化身。要實現這種功能,TCP不能給處於TIME_WAIT狀態的連接啟動新的連接。TIME_WAIT的持續時間是2MSL,保證在建立新的連接之前老的重復分組在網絡中消逝。這個規則有一個例外:如果到達的SYN的序列號大於前一個連接的結束序列號,源自Berkeley的實現將給當前處於TIME_WAIT狀態的連接啟動新的化身。

最初在看《Unix網絡編程》 的時候看到這個狀態,但是在項目中發現對這個狀態的理解有誤,特別是第二個理由。原本認為在TIME_WAIT狀態下肯定不會再使用相同的五元組(協議類型,源目的IP、源目的端口號)建立一個新的連接,看書還是不認真啊!為了加深理解,決定結合內核代碼,好好來看下內核在TIME_WAIT狀態下的處理。其實TIME_WAIT存在的第二個原因的解釋更多的是從被動關閉一方的角度來說明的。如果是執行主動關閉的是客戶端,客戶端戶進入TIME_WAIT狀態,假設客戶端重用端口號來和服務器建立連接,內核會不會允許客戶端來建立連接?內核如何來處理這種情況?書本中不會對這些點講的那麼詳細,要從內核源碼中來找答案。

我們先來看服務器段進入TIME_WAIT後內核的處理,即服務器主動關閉連接。TCP層的接收函數是tcp_v4_rcv(),和TIME_WAIT狀態相關的主要代碼如下所示:

int tcp_v4_rcv(struct sk_buff *skb)
{
    ......

    sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
    if (!sk)
        goto no_tcp_socket;
process:
    if (sk->sk_state == TCP_TIME_WAIT)
        goto do_time_wait;   
        ......

discard_it:
    /* Discard frame. */
    kfree_skb(skb);
    return 0;
    ......
do_time_wait:
    ......

    switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
    case TCP_TW_SYN: {
        struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
                            &tcp_hashinfo,
                            iph->daddr, th->dest,
                            inet_iif(skb));
        if (sk2) {
            inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
            inet_twsk_put(inet_twsk(sk));
            sk = sk2;
            goto process;
        }
        /* Fall through to ACK */
    }
    case TCP_TW_ACK:
        tcp_v4_timewait_ack(sk, skb);
        break;
    case TCP_TW_RST:
        goto no_tcp_socket;
    case TCP_TW_SUCCESS:;
    }
    goto discard_it;
}

Copyright © Linux教程網 All Rights Reserved