zoukankan      html  css  js  c++  java
  • linux TCP数据包重传过程----小结

     于TCP/IP协议栈的TCP协议的重传功能是由在linux内核源码(net/ipv4/tcp_output.c)中的函数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;
    	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.
    	 */
    	 	//说明在发送缓存区中消耗了许多内存去做其他的工作(比如分片等,只有1/4的缓存才是保留给这些工作的),暂时不能重传
    	if (atomic_read(&sk->sk_wmem_alloc) >
    	    min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
    		return -EAGAIN;
    	//检测重传的段,接收方是否已经收到其部分或者全部,如果收到则说明有bug ,否者就调整TCP段的负荷,即删除SKB缓存区
    	//前面部分已经接收到的数据
    	if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
    		if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
    			BUG();
    		if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))
    			return -ENOMEM;
    	}
    	//根据目的地址等条件获取路由,如果获取路由失败就不能发送
    	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
    		return -EHOSTUNREACH; /* Routing failure or similar. */
    
    	cur_mss = tcp_current_mss(sk);
    
    	/* 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.
    	 */
    	 //如果接收方已经减小了窗口,并且带重传的SKB已经不在新的窗口内,则不能重传该SKB,
    	 //有一种情况例外,就是接收方的接受窗口减少为0,在这种情况下会发送0窗口探测段
    	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. */
    	} else {
    		int oldpcount = tcp_skb_pcount(skb);
    
    		if (unlikely(oldpcount > 1)) {
    			tcp_init_tso_segs(sk, skb, cur_mss);
    			tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
    		}
    	}
    
    	tcp_retrans_try_collapse(sk, skb, cur_mss);
    
    	/* 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 & TCPHDR_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);//发送SKB
    
    	if (err == 0) {
    		/* Update global TCP statistics. */
    		TCP_INC_STATS(sock_net(sk), 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.
    ");
    		}
    #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 += tcp_skb_pcount(skb);
    
    		/* 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;
    }
    

    这里注意的到函数tcp_retransmit_skb()最终的是调用函数tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);将SKB发送出去,而构造TCP的头部信息在函数tcp_transmit_skb()中,下面是函数tcp_transmit_skb()构造TCP头部的片段 所以也就是说在发送队列中的SKB是没有头部的,这也是方便了选择重传等功能

    	/* Build TCP header and checksum it. */
    	th = tcp_hdr(skb);
    	th->source		= inet->inet_sport;
    	th->dest		= inet->inet_dport;
    	th->seq			= htonl(tcb->seq);
    	th->ack_seq		= htonl(tp->rcv_nxt);
    	*(((__be16 *)th) + 6)	= htons(((tcp_header_size >> 2) << 12) |
    					tcb->flags);

                            


    from:http://blog.csdn.net/scdxmoe/article/details/17764109

  • 相关阅读:
    【LeetCode】048. Rotate Image
    【LeetCode】036. Valid Sudoku
    【LeetCode】060. Permutation Sequence
    【LeetCode】001. Two Sum
    【LeetCode】128. Longest Consecutive Sequence
    【LeetCode】081. Search in Rotated Sorted Array II
    【LeetCode】033. Search in Rotated Sorted Array
    顺时针打印矩阵
    矩形覆盖
    二维数组中的查找
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332314.html
Copyright © 2011-2022 走看看