9.3 重传定时器

9.3.1 Why

        TCP在发送SYN、FIN以及数据包时为了保证可靠传输,会先将它们放入发送队列再发送副本到网络中,一旦发现数据丢失(比如连续收到多个ack_seq号相同的ACK)则重传发送队列中的skb。如果丢失发现机制失效了呢(比如ACK丢失),这时就需要重传定时器在指定的时间内重传数据,否则数据传输就可能会阻塞。

9.3.2 When

        设置重传定时器的时机有:

(1)调用tcp_check_sack_reneging处理虚假SACK事件时:

1906 static bool tcp_check_sack_reneging(struct sock *sk, int flag)
1907 {
1908     if (flag & FLAG_SACK_RENEGING) {
1909         struct inet_connection_sock *icsk = inet_csk(sk);
1910         NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
1911 
1912         tcp_enter_loss(sk, 1);
1913         icsk->icsk_retransmits++;
1914         tcp_retransmit_skb(sk, tcp_write_queue_head(sk));
1915         inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
1916                       icsk->icsk_rto, TCP_RTO_MAX);
1917         return true;
1918     }
1919     return false;
1920 }
        虚假SACK是指:最新收到的ACK的ack_seq指向已记录的SACK块,这说明记录的SACK并没有反应接收方的真实的状态,也就是说接收方现在已经处于严重拥塞的状态或者在处理上有bug,从而删除了乱序队列中的数据(这些数据之前是在SACK选项中发送过来的)。因为按照正常的逻辑流程,接收的ACK不应该指向已记录的SACK,而应该指向SACK后面未接收的地方(因为被SACK的报文是已经放入接收方的乱序队列中,如果收到缺失的段正常情况下会与乱序报文一起交付接收队列,从而使ack_seq指向被SACK的报文的后面)。所以接下来就按照超时重传的方式去处理。

(2)调用tcp_rearm_rto更新RTO时:

2926 void tcp_rearm_rto(struct sock *sk)
2927 {
2928     const struct inet_connection_sock *icsk = inet_csk(sk);
2929     struct tcp_sock *tp = tcp_sk(sk);
2930 
2931     /* If the retrans timer is currently being used by Fast Open
2932      * for SYN-ACK retrans purpose, stay put.
2933      */
2934     if (tp->fastopen_rsk)
2935         return;
2936 
2937     if (!tp->packets_out) {    //网络中没有已发送的数据
2938         inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
2939     } else {
2940         u32 rto = inet_csk(sk)->icsk_rto;
2941         /* Offset the time elapsed after installing regular RTO */
2942         if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
2943             icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
2944             struct sk_buff *skb = tcp_write_queue_head(sk);
2945             const u32 rto_time_stamp = TCP_SKB_CB(skb)->when + rto;
2946             s32 delta = (s32)(rto_time_stamp - tcp_time_stamp);
2947             /* delta may not be positive if the socket is locked
2948              * when the retrans timer fires and is rescheduled.
2949              */
2950             if (delta > 0)
2951                 rto = delta;
2952         }
2953         inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto,
2954                       TCP_RTO_MAX);
2955     }
2956 }
        而调用tcp_rearm_rto并安装重传定时器的常见条件有:

1)收到ACK并确认掉数据时且仍然有未确认的数据时:

3001 static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
3002                    u32 prior_snd_una)
3003 {
...
3095     if (flag & FLAG_ACKED) {
...
3105         tcp_rearm_rto(sk);
2)收到合法ACK并安装了ER定时器或丢失探测定时器时:
3325 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
3326 {
...
3358     if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
3359         icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)
3360         tcp_rearm_rto(sk);
...
3)发送了新数据并调用tcp_event_new_data_sent函数时:
 72 static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
  73 {
  74     struct inet_connection_sock *icsk = inet_csk(sk);
  75     struct tcp_sock *tp = tcp_sk(sk);
  76     unsigned int prior_packets = tp->packets_out;
  77 
  78     t
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值