日期:2014-05-16  浏览次数:20929 次

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


当知道需要重传数据结的时候执行这个函数:

对于函数tcp_xmit_retransmit_queue:需要重传哪些包呢到底?

首先是lost、标记的包;

然后还需要处理:之前发送过的但是尚未收到确认的包(向前重传),或者新数据,在这两者之间有一个选择

/* This gets called after a retransmit timeout, and the initially
 * retransmitted data is acknowledged.  It tries to continue
 * resending the rest of the retransmit queue, until either
 * we've sent it all or the congestion window limit is reached.
 * If doing SACK, the first ACK which comes back for a timeout
 * based retransmit packet might feed us FACK information again.
 * If so, we use it to avoid unnecessarily retransmissions.
 */
void tcp_xmit_retransmit_queue(struct sock *sk)
{
         const struct inet_connection_sock *icsk = inet_csk(sk);
         struct tcp_sock *tp = tcp_sk(sk);
         struct sk_buff *skb;
         int packet_cnt;
 
         if (tp->retransmit_skb_hint) {        // 如果有重传信息
                 skb = tp->retransmit_skb_hint;
                 packet_cnt = tp->retransmit_cnt_hint; // 保存cnt值
         } else {
                 skb = tcp_write_queue_head(sk); // 发送队列
                 packet_cnt = 0;
         }
         // 第一步,如果有丢失的包,那么需要重传
         /* First pass: retransmit lost packets. */
         if (tp->lost_out) {  // lost_out > 0
                 tcp_for_write_queue_from(skb, sk) {   // 遍历
                         __u8 sacked = TCP_SKB_CB(skb)->sacked;   // 获得sacked标识
 
                         if (skb == tcp_send_head(sk))
                                break;
                         /* we could do better than to assign each time */
                         tp->retransmit_skb_hint = skb;           // 更新两个值
                         tp->retransmit_cnt_hint = packet_cnt;
 
                         /* Assume this retransmit will generate
                          * only one packet for congestion window
                          * calculation purposes.  This works because
                          * tcp_retransmit_skb() will chop up the
                          * packet to be MSS sized and all the
                          * packet counting works out.
                          */
                         if (tcp_packets_in_flight(tp) >= tp->snd_cwnd)   // 如果传输中的报文数量 > 窗口数量,那么没有必要再发送数据
                                 return;
 
                         if (sacked & TCPCB_LOST) {  // 如果是LOST标识
                                 if (!(sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS))) {  // 如果丢失了 && 没有被选择确认或者重传
                                         if (tcp_retransmit_skb(sk, skb)) {       // 重传该数据函数!!!最后再看(1)
                                                 tp->retransmit_skb_hint = NULL;  // 重传之后重置这个值
                                                 return;                          // 返回
                                         }
                                         if (icsk->icsk_ca_state != TCP_CA_Loss)
                                                 NET_INC_STATS_BH(LINUX_MIB_TCPFASTRETRANS);
                                         else
                                                 NET_INC_STATS_BH(LINUX_MIB_TCPSLOWSTARTRETRANS);
 
                                         if (skb == tcp_write_queue_head(sk))   // 如果是第一个重传数据,那么重置重传计数器!!!
                                                 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                                                                           inet_csk(sk)->icsk_rto,
                                                                           TCP_RTO_MAX);
                                 }
 
                                 packet_cnt += tcp_skb_pcount(skb);    // 重传数量
                                 if (packet_cnt >= tp->lost_out)  // 大于lost的数量,那么break;下面就不是lo