博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux TCP数据包重传过程----小结
阅读量:5922 次
发布时间:2019-06-19

本文共 4162 字,大约阅读时间需要 13 分钟。

 于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.\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 += 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:

转载于:https://www.cnblogs.com/hehehaha/p/6332314.html

你可能感兴趣的文章
fail2ban 帮助postfix 过滤恶意IP
查看>>
Simple Proxy Server (Java)
查看>>
Kafka消费的几种方式--low-level SimpleConsumer
查看>>
解决mysql数据库不能支持中文的问题
查看>>
VMware14虚拟机秘钥
查看>>
JVM -verbose参数详解
查看>>
CentOS LInux启动关闭和服务管理
查看>>
Eclipse中10个最有用的快捷键组合
查看>>
java与xml
查看>>
Redis Sentinel机制与用法(二)
查看>>
ls命令实际使用
查看>>
磁盘及磁盘阵列系统选择
查看>>
Javascript异步数据的同步处理方法
查看>>
9. Palindrome Number(回文数)(leetcode)
查看>>
MySQL之自定义函数实例讲解
查看>>
用.htaccess获取文件夹和文件名
查看>>
自我提升mysql
查看>>
步步为营之——建造者模式(Builder)
查看>>
快速排序——Java
查看>>
unity游戏与我
查看>>