Linux 内核网络协议栈 ------ tcp重传数据包 tcp_retransmit_skb 函数


/* This retransmits one SKB.  Policy decisions and retransmit queue
 * state updates are done by the caller.  Returns non-zero if an
 * error occurred which prevented the send.
 */
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
{
         struct tcp_sock *tp = tcp_sk(sk);
         struct inet_connection_sock *icsk = inet_csk(sk);
         unsigned int cur_mss = tcp_current_mss(sk, 0);
         int err;
 
         /* Inconslusive MTU probe */
         if (icsk->icsk_mtup.probe_size) {
                 icsk->icsk_mtup.probe_size = 0;
         }
 
         /* Do not sent more than we queued. 1/4 is reserved for possible  
          * copying overhead: fragmentation, tunneling, mangling etc.
          */ // 如果消耗很多的内存做其他事,那么就没有多余的来做队列的处理了~
         if (atomic_read(&sk->sk_wmem_alloc) >                                     // sk_wmem_alloc:传输队列大小
             min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))   // sk_wmem_queud:固定的队列大小
                 return -EAGAIN;
 
         if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {// 若这样,说明是有一部分数据才需要重传,形如:seq---snd_una---end_seq,前面一半已收到ACK
                 if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))  // 若这样,说明全部ACK,无需重传,BUG
                         BUG();
                 if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))  // 一些控制信息检查
                         return -ENOMEM;
         }
 
         /* If receiver has shrunk his window, and skb is out of
          * new window, do not retransmit it. The exception is the
          * case, when window is shrunk to zero. In this case
          * our retransmit serves as a zero window probe.
          */
         if (!before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))    // 如果数据在窗口后面,不会发送
             && TCP_SKB_CB(skb)->seq != tp->snd_una)   
                return -EAGAIN;
 
         if (skb->len > cur_mss) {   // 如果skb长度 > MSS
                 if (tcp_fragment(sk, skb, cur_mss, cur_mss))   // 先分片。再传送
                         return -ENOMEM; /* We‘ll try again later. */
         }
 
         /* Collapse two adjacent packets if worthwhile and we can. */  // 我*,这么多条件
         if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&              // SYN包
             (skb->len < (cur_mss >> 1)) &&                             // 长度<半个MSS
             (tcp_write_queue_next(sk, skb) != tcp_send_head(sk)) &&    // 不是结尾
             (!tcp_skb_is_last(sk, skb)) &&                             // 不是最后一个
             (skb_shinfo(skb)->nr_frags == 0 &&                         // 没有分页数据
              skb_shinfo(tcp_write_queue_next(sk, skb))->nr_frags == 0) &&
             (tcp_skb_pcount(skb) == 1 &&                               // gso_segs=1
              tcp_skb_pcount(tcp_write_queue_next(sk, skb)) == 1) &&
             (sysctl_tcp_retrans_collapse != 0))
                 tcp_retrans_try_collapse(sk, skb, cur_mss);            // 这个函数不是很明白,待看~~~~~~~~~~~~~~~~~~~~~~~~~
 
         if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) // 根据目的地址等条件获取路由,如果获取路由失败就不能发送
                 return -EHOSTUNREACH; /* Routing failure or similar. */
 
         /* Some Solaris stacks overoptimize and ignore the FIN on a
          * retransmit when old data is attached.  So strip it off
          * since it is cheap to do so and saves bytes on the network.
          *///Solaris系统的协议栈有时候会忽略重传SKB上带有的FIN标志的payload,将payload全部剥离掉,节省网络流量
         if (skb->len > 0 &&
             (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
             tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
                 if (!pskb_trim(skb, 0)) {
                         /* Reuse, even though it does some unnecessary work */
                         tcp_init_nondata_skb(skb, TCP_SKB_CB(skb)->end_seq - 1,
                                              TCP_SKB_CB(skb)->flags);
                         skb->ip_summed = CHECKSUM_NONE;
                 }
         }
 
         /* Make a copy, if the first transmission SKB clone we made
          * is still in somebody‘s hands, else make a clone.
          */
         TCP_SKB_CB(skb)->when = tcp_time_stamp;
 
         err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);    // 这个才是正在的传输函数~~~~~~~~~~~~~~~~~~~~后面再说~~~~~~~~~~~~~~~
                                                            // 这个函数就是将数据包发送到下面一层,再慢慢传输出去~~~~~~~~~~~~~~
         if (err == 0) {    // 发送成功,那么就需要更新TCP统计信息
                 /* Update global TCP statistics. */  
                 TCP_INC_STATS(TCP_MIB_RETRANSSEGS);
 
                 tp->total_retrans++;   // 整体重传数量++
 
#if FASTRETRANS_DEBUG > 0
                 if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
                         if (net_ratelimit())
                                 printk(KERN_DEBUG "retrans_out leaked.\n");
                 }
#endif
                 if (!tp->retrans_out)
                         tp->lost_retrans_low = tp->snd_nxt;
                 TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;
                 tp->retrans_out += tcp_skb_pcount(skb);   // 重传出去的数量+=。。。
 
                 /* Save stamp of the first retransmit. */
                 if (!tp->retrans_stamp)
                         tp->retrans_stamp = TCP_SKB_CB(skb)->when;  // 第一次重传时间戳
 
                 tp->undo_retrans++;
 
                 /* snd_nxt is stored to detect loss of retransmitted segment,
                  * see tcp_input.c tcp_sacktag_write_queue().
                  */
                 TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt;
         }
         return err;
}
 



Linux 内核网络协议栈 ------ tcp重传数据包 tcp_retransmit_skb 函数,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。