超时重传相关问题学习

注:内核版本4.14.119

一、问题

1. 超时重传的timeout设置和次数
1.1 RTO设置

超时重传RTO的值和rtt有关,即通过rtt可以计算出RTO。RTO的取值范围是[TCP_RTO_MIN(HZ/5), TCP_RTO_MAX(120HZ)],RTO的具体计算此处不展开。

1.2重传次数

1.2.1 三次握手的重传次数
在三次握手时,重传次数时可以确定的。

net.ipv4.tcp_syn_retries = 6        //for client, 用于在syn发送阶段
net.ipv4.tcp_synack_retries = 5     //for server, 用于在yn-ack发送阶段

1.2.2 在数据通信过程中,重传次数不是一个确定的值,有若干因素同时确定该值:a是以下这些值(sysctl -a), b是网络状况,网络状况影响RTT,继而影响重传的时间间隔。但是我们发现,如果tcp_retries2 = 15,在702.2秒内,如果没有重传成功,那么就认为链路已经断开。

net.ipv4.tcp_retries1 = 3           //暂时不明
net.ipv4.tcp_retries2 = 15          //在通信过程中。
static bool retransmits_timed_out(struct sock *sk,
				  unsigned int boundary,
				  unsigned int timeout)
{
	const unsigned int rto_base = TCP_RTO_MIN;
	unsigned int linear_backoff_thresh, start_ts;

	if (!inet_csk(sk)->icsk_retransmits)
		return false;

	start_ts = tcp_sk(sk)->retrans_stamp;
	if (unlikely(!start_ts))
		start_ts = tcp_skb_timestamp(tcp_write_queue_head(sk));
    //boundary就是tcp_retries2或tcp_retries1,可见这里和最终的可重传次数不是相等的
	if (likely(timeout == 0)) {
		linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base); //linear_backoff_thresh = 9
        
		if (boundary <= linear_backoff_thresh)
		    // (2^9 -1) * HZ / 5 = 511 * 0.2s = 102.2s (最大值 )
			timeout = ((2 << boundary) - 1) * rto_base;
		else
		    // 102.2s + 5 * 120s = 702.2s
			timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +
				(boundary - linear_backoff_thresh) * TCP_RTO_MAX;
	}
	return (tcp_time_stamp(tcp_sk(sk)) - start_ts) >= jiffies_to_msecs(timeout);
}

static int tcp_write_timeout(struct sock *sk)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	struct net *net = sock_net(sk);
	bool expired, do_reset;
	int retry_until;
	//三次握手阶段
	if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
		if (icsk->icsk_retransmits) {
			dst_negative_advice(sk);
			if (tp->syn_fastopen || tp->syn_data)
				tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
			if (tp->syn_data && icsk->icsk_retransmits == 1)
				NET_INC_STATS(sock_net(sk),
					      LINUX_MIB_TCPFASTOPENACTIVEFAIL);
		} else if (!tp->syn_data && !tp->syn_fastopen) {
			sk_rethink_txhash(sk);
		}
		retry_until = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_syn_retries;
		expired = icsk->icsk_retransmits >= retry_until;
	} else {//正常通信阶段
		//sysctl_tcp_retries1不太理解
		if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1, 0)) {
			/* Some middle-boxes may black-hole Fast Open _after_
			 * the handshake. Therefore we conservatively disable
			 * Fast Open on this path on recurring timeouts after
			 * successful Fast Open.
			 */
			if (tp->syn_data_acked) {
				tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
				if (icsk->icsk_retransmits == net->ipv4.sysctl_tcp_retries1)
					NET_INC_STATS(sock_net(sk),
						      LINUX_MIB_TCPFASTOPENACTIVEFAIL);
			}
			/* Black hole detection */
			tcp_mtu_probing(icsk, sk);

			dst_negative_advice(sk);
		} else {
			sk_rethink_txhash(sk);
		}
		retry_until = net->ipv4.sysctl_tcp_retries2;
		if (sock_flag(sk, SOCK_DEAD)) {
			const bool alive = icsk->icsk_rto < TCP_RTO_MAX;

			retry_until = tcp_orphan_retries(sk, alive);
			do_reset = alive ||
				!retransmits_timed_out(sk, retry_until, 0);

			if (tcp_out_of_resources(sk, do_reset))
				return 1;
		}
		expired = retransmits_timed_out(sk, retry_until,
						icsk->icsk_user_timeout);
	}
	if (expired) {
		/* Has it gone just too far? */
		tcp_write_err(sk);
		return 1;
	}
	return 0;
}
2. 超时重传的顺序,如果多个包都发生重传,如何重传。例如一次重传一个包还是多个包?

先说明一下,重传包每个都只有1mss,即不会使用gso,一次重传一个包,当然可以连续发送多个包(连续发送的包的数量由拥塞窗口控制). 默认情况下,每个发包最大就是1514(mss 1460). 当然收包处是可以gro(generic-receive-offload).
举个例子: 发包1~100, 收包 1-90,其中91-100丢失了. 此时发生超时了,sender重传91,receiver收到91. sender认为链路已正常,接下来sender会发送101之后的包,例如发101-102,很不幸receiver会发一个DACK报文(携带SACK option,表示收到101-102包),提示需要92号报文。sender只好把92-100的包先发了 (以上为实验测试所得,可能不适应各个情况)
在这里插入图片描述

3. 超时包重传的优先级和当前待发送包的关系?

这个很明显,当然是先发送超时包,因为ACK机制决定收包是有序的,因此需要先发送超时的包。

4. 重传的报文放置何处,如何区分正常包和重传包?

没有专门的重传队列。所有待发送或待重传的包都放在sock.sk_write_queue中。根据sock.sk_send_head和sock.sk_write_queue可以确认待重传的skb(待确认的skb)

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值