您的位置:首页 > 理论基础 > 计算机网络

TCP 重传计时器的一点看法

2014-04-08 23:22 246 查看
问题:总是会看到这句话:When TCP sends a segment, it creates a Retransmission timer for that particular segment。

在一个TCP连接中,TCP每发送一个报文段, 就对此报文段设置一个超时重传计时器。

那么当发送多个报文段时,

1.由于需要对每个报文段设置重传计时器,因此有多个重传计时器?2.还是书上说的,只有一个重传计时器?为什么?

在网上找了一些资料,内容较乱,特此整理了一下。。。。

毫无疑问的是在一个TCP连接中,只会有一个重传计时器

否则的话,对于某个特定服务器,假如在某时刻发出了10000个分段,难道在TCP连接中分别有10000个重传计时器的存在?答案是明显的。。。

那么,下一个问题接踵而来: TCP的重传计时器是如何工作的呢?

像在如下场景里:

发送方假设连续发送了S1,S2,S3这3个分段,TCP重传定时器在S1发送时被设置,然后再发送S2时,此时因为重传定时器已经启动,所以不再处理S2,直到收到包含S1的ack后,重传定时器被重置(开始计时)。

那么如果的S1发送后,在正常收到ack之前,发送的S2丢失掉了。对于S2,重传计时器是不会计时的,那么发送端怎么知道要重传呢?



在网上看到一种观点:

大多数时候,TCP发送的包不止两个,假设发送了第三个包了,此时接收方会发送一个重复的ack(S1的ack),发送方在接收到三次重复ack时(需要继续发送S4,S5),就会重传S2(快速重传算法)。使用这种方法,S2可以进行重传。如下图:



咋看之下,可以解决S2的重传问题。但是如果仅仅只有S1,S2两个包时,上述方法并不会适用S2的重传(永远不会得到三个连续ACK)。现在,让我们回到 TCP 重传计时器 的重置问题:闲话不说,直接看代码。

Linux的tcp代码,显然代码表示和重传有关的定时器就只有一个

  259 void inet_csk_init_xmit_timers(struct sock *sk,

  260                    void (*retransmit_handler)(unsigned long),

  261                    void (*delack_handler)(unsigned long),

  262                    void (*keepalive_handler)(unsigned long))

  263 {   

  264     struct inet_connection_sock *icsk = inet_csk(sk);

  265     

  266     init_timer(&icsk->icsk_retransmit_timer);/ /重传计时器

  267     init_timer(&icsk->icsk_delack_timer); //delayed ack定时器

  268     init_timer(&sk->sk_timer);

  269     

  270    icsk->icsk_retransmit_timer.function = retransmit_handler;  //重传计时器

  271     icsk->icsk_delack_timer.function     = delack_handler;

  272     sk->sk_timer.function            = keepalive_handler;

  273     

  274     icsk->icsk_retransmit_timer.data =

  275         icsk->icsk_delack_timer.data =

  276             sk->sk_timer.data  = (unsigned long)sk;

  277 

  278     icsk->icsk_pending = icsk->icsk_ack.pending = 0;

  279 }

现在我们来看tcp_event_new_data_sent是如何启动定时器的:

static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)  

{  

    struct tcp_sock *tp = tcp_sk(sk);  

    unsigned int prior_packets = tp->packets_out;  

  

    tcp_advance_send_head(sk, skb);  

    tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;  

  

    /* Don't override Nagle indefinately with F-RTO */  

    if (tp->frto_counter == 2)  

        tp->frto_counter = 3;  

//关键在这里.  

    tp->packets_out += tcp_skb_pcount(skb);  

    if (!prior_packets)  

        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,  

                      inet_csk(sk)->icsk_rto, TCP_RTO_MAX);  



从代码中,我们可以看到只有在tcp_event_new_data_sent里当packets_out = 0的时候,会reset retrans timer。

而packets_out则是在当前窗口,已经发送但是还没有收到ACK的segments。即如果发送了很多段,如果前面的段没有确认,那么后面发送的时候不会重启这个定时器的。

因此,如下图所示,我们得到:

在TCP的重传计时器的问题上,我们分为两种情况:

1.如果先发了S1,在没有收到ack的情况下,发送S2的时候,此时packets_out != 0,我们是不会去reset这个timer的,那么如果S2丢失的话,packet_outs是不会减到0(因为没有收到S2的ACK),那么这个timer最后总会timeout的。(超时重传机制)。

而这三个segment又是差不多同时发送的,所以这个timeout value对S2来说也是正确的。

2.如果先发了S1, 等收到ack后再发S2的话,那么retrans timer就会在发S1和S2的时候都会被设置。因为收到S1的ack后,packet_out会减为0。下一个S2的话,又是下一个窗口的第一个包。





综上,我们知道:

1.一个TCP连接中,只会有一个重传计时器。

2.在TCP数据发送过程里,当packets_out = 0的时候,才会重置TCP重传计时器.即如果发送了很多段,如果前面的段没有确认,那么后面发送的时候不会重启这个定时器的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: