TCP 重传计时器的一点看法

问题:总是会看到这句话: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重传计时器.即如果发送了很多段,如果前面的段没有确认,那么后面发送的时候不会重启这个定时器的。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值