TCP的RTO值估算

2023-12-19 09:48
文章标签 tcp rto 估算

本文主要是介绍TCP的RTO值估算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大致介绍一下Linux内核实现的RTO值计算方式,以及与RFC6298的不同之处。

RTT估算

static void tcp_rtt_estimator(struct sock *sk, long mrtt_us)
{struct tcp_sock *tp = tcp_sk(sk);long m = mrtt_us; /* RTT */u32 srtt = tp->srtt_us;/*  The following amusing code comes from Jacobson's article in SIGCOMM '88.*  Note that rtt and mdev are scaled versions of rtt and mean deviation.*  This is designed to be as fast as possible  m stands for "measurement".**  On a 1990 paper the rto value is changed to:*  RTO = rtt + 4 * mdev** Funny. This algorithm seems to be very broken.* These formulae increase RTO, when it should be decreased, increase too slowly,* when it should be increased quickly, decrease too quickly etc. * I guess in BSD RTO takes ONE value, so that it is absolutely does not matter * how to _calculate_ it. Seems, it was trap that VJ failed to avoid. 8)*/

如果SRTT之前已经有值,新的SRTT的值等于7/8倍的原有SRTT值与1/8倍的新测量的RTT值之和,即新测量值在SRTT中比重为八分之一。 由于m为测量值,为原值,而srtt为SRTT值的8倍,以下代码计算新的srtt值, 其等于: srtt + m - srrt1/8,即srtt7/8 + m,最终结果为8倍的SRTT,即新的srtt的值。RFC6298定义的SRTT计算公式如下,其中alpha为1/8,R’为RTT测量值。

	SRTT <- (1 - alpha) * SRTT + alpha * R’

之后,计算MDEV(Mean DEViation)的值,其表示RTT的变化情况,注意RFC6298中并没有定义MDEV值,而是使用RTTVAR值。RTTVAR的计算公式如下,其中beta取值为1/4。其中SRTT为上次计算的值,而不是更新之后的值。

    RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R’|

内核中MDEV值的计算类似于RFC6298中的RTTVAR(Round-Trip Time VARiation)计算公式,不同的是,如果mdev测量值快速减小,幅度超过记录的MDEV(变量mdev_us表示4倍的MDEV)值时,内核将减低mdev测量值在MDEV值中的比重(再减低1/8),防止因RTT急剧减小导致RTO值增加(参见RTO值的计算),此情况下mdev测量值在MDEV新值中的比重为:(alphabeta = 1/81/4 = 1/32)。

如下计算公式,其中mdev_m表示本次的mdev测量值,mdev_us为最终的MDEV值。

    mdev_us + (mdev_m - mdev_us/4)/8= mdev_us + mdev_m/8 - mdev_us/32 = mdev_us *31/32 + mdev_m/8等同于以下,注意mdev_us中保存的为4倍的MDEV值。MDEV * 31/32 + mdev_m/32

否则,如果本次测量的mdev值没有急剧下降,或者其值大于等于零,使用RFC中定义的计算公式获取MDEV值(得到的mdev_us为4倍MDEV): mdev_us + (mdev_m - mdev_us/4) = mdev_us *3 /4 + mdev_m。

    if (srtt != 0) {m -= (srtt >> 3);   /* m is now error in rtt est */srtt += m;      /* rtt = 7/8 rtt + 1/8 new */if (m < 0) {m = -m;     /* m is now abs(error) */m -= (tp->mdev_us >> 2);   /* similar update on mdev *//* This is similar to one of Eifel findings.* Eifel blocks mdev updates when rtt decreases.* This solution is a bit different: we use finer gain* for mdev in this case (alpha*beta).* Like Eifel it also prevents growth of rto,* but also it limits too fast rto decreases,* happening in pure Eifel.*/if (m > 0)m >>= 3;} else {m -= (tp->mdev_us >> 2);   /* similar update on mdev */}tp->mdev_us += m;       /* mdev = 3/4 mdev + 1/4 new */

不同于RFC6298,以下使用MDEV值更新rttvar的值,只要得到的新MDEV值大于记录的最大值,更新MDEV最大值,并且在大于之前RTTVAR时,同步更新RTTVAR值,即RTTVAR值为MDEV最大值,每次测量都由可能更新RTTVAR值。这样可以避免内核在大的窗口下对过多的报文进行测量,而RTT变化很小,导致的RTO值减少的问题。

如果当前未确认的数据序号SND.UNA位于记录的rtt_seq序号之后,说明进入了下一个发送窗口期(新的RTT),更新rtt_seq为新的SND.NXT值,并且,如果mdev_max_us小于rttvar_us的值,将rttvar_us的值更新为3/4倍的rttvar_us值加上1/4倍的mdev_max_us的值。

rttvar - (rttvar - mdev_max)/4
= rttvar - rttvar *1 /4 + mdev_max/4
= rttvar * 3/4 + mdev_max/4

以上可见,内核仅允许RTTVAR值在一个RTT周期内减少一次,这样可以避免在窗口过大的情况下,进行的测量次数过多,而RTT变化很小的情况下,RTO减小的风险,造成不必要的重传。

        if (tp->mdev_us > tp->mdev_max_us) {tp->mdev_max_us = tp->mdev_us;if (tp->mdev_max_us > tp->rttvar_us)tp->rttvar_us = tp->mdev_max_us;}if (after(tp->snd_una, tp->rtt_seq)) {if (tp->mdev_max_us < tp->rttvar_us)tp->rttvar_us -= (tp->rttvar_us - tp->mdev_max_us) >> 2;tp->rtt_seq = tp->snd_nxt;tp->mdev_max_us = tcp_rto_min_us(sk);}

如果SRTT没有值,表明这是第一次测量,RFC6298规定SRTT和RTTVAR的初始值如下,R为首次RTT测量值。

    SRTT <- RRTTVAR <- R/2

内核中与RFC基本相同,将srtt值设置为本次测量的RTT时间值,初始偏差值mdev_us(Mean DEViation)等于SRRT值的一倍;而rttvar_us值等于MDEV与TCP_RTO_MIN(tcp_rto_min_us默认200ms)两者之间的较大值。最后记录下RTT计算时的SND.NXT值。与RFC6298不同,Linux内核使用的RTO最小值为200ms,而RFC中定义为1秒。

    } else {/* no previous measure. */srtt = m << 3;      /* take the measured time to be rtt */tp->mdev_us = m << 1;   /* make sure rto = 3*rtt */tp->rttvar_us = max(tp->mdev_us, tcp_rto_min_us(sk));tp->mdev_max_us = tp->rttvar_us;tp->rtt_seq = tp->snd_nxt;}tp->srtt_us = max(1U, srtt);
}

RTO值计算

以下RTO计算函数,其等于SRTT加上4倍的RTTVAR值。RFC6298中的计算公式为:RTO <- SRTT + max (G, K*RTTVAR),其中G为时钟精度,K等于4。

static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
{return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}

在没有退避的情况下,内核中使用函数tcp_set_rto设置RTO值。RTTVAR不太可能小于50毫秒,其中solaris和freebsd不稳定的ACK回复方式使其不可能发生,但是这与延时ACK无关,因为在拥塞窗口大于2时,不会等到延时ACK的定时器超时的时候发送ACK,3个报文完全可以触发接收端的ACK回复。实际上,Linux-2.4在一些情况下也会产生不稳定的ACK。

所以RTO计算中的4倍RTTVAR至少为200毫秒,RTO值不会小于TCP_RTO_MIN(HZ/5)。函数tcp_bound_rto设置RTO上限值。

static void tcp_set_rto(struct sock *sk)
{   const struct tcp_sock *tp = tcp_sk(sk);/* Old crap is replaced with new one. 8)** More seriously:* 1. If rtt variance happened to be less 50msec, it is hallucination.*    It cannot be less due to utterly erratic ACK generation made*    at least by solaris and freebsd. "Erratic ACKs" has _nothing_*    to do with delayed acks, because at cwnd>2 true delack timeout*    is invisible. Actually, Linux-2.4 also generates erratic*    ACKs in some circumstances.*/inet_csk(sk)->icsk_rto = __tcp_set_rto(tp);/* 2. Fixups made earlier cannot be right.*    If we do not estimate RTO correctly without them,*    all the algo is pure shit and should be replaced*    with correct one. It is exactly, which we pretend to do.*//* NOTE: clamping at TCP_RTO_MIN is not required, current algo* guarantees that rto is higher.*/tcp_bound_rto(sk);

如下函数tcp_bound_rto将RTO值的上限设置在120秒。

static inline void tcp_bound_rto(const struct sock *sk)
{   if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)inet_csk(sk)->icsk_rto = TCP_RTO_MAX;
}
#define TCP_RTO_MAX ((unsigned)(120*HZ))

内核版本 5.0

这篇关于TCP的RTO值估算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511742

相关文章

QT实现TCP客户端自动连接

《QT实现TCP客户端自动连接》这篇文章主要为大家详细介绍了QT中一个TCP客户端自动连接的测试模型,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录版本 1:没有取消按钮 测试效果测试代码版本 2:有取消按钮测试效果测试代码版本 1:没有取消按钮 测试效果缺陷:无法手动停

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么                      🎵 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time""github.com/ClickHouse/clickhouse-go/v2")func main(

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

图解TCP三次握手|深度解析|为什么是三次

写在前面 这篇文章我们来讲解析 TCP三次握手。 TCP 报文段 传输控制块TCB:存储了每一个连接中的一些重要信息。比如TCP连接表,指向发送和接收缓冲的指针,指向重传队列的指针,当前的发送和接收序列等等。 我们再来看一下TCP报文段的组成结构 TCP 三次握手 过程 假设有一台客户端,B有一台服务器。最初两端的TCP进程都是处于CLOSED关闭状态,客户端A打开链接,服务器端

网络原理之TCP协议(万字详解!!!)

目录 前言 TCP协议段格式 TCP协议相关特性 1.确认应答 2.超时重传 3.连接管理(三次握手、四次挥手) 三次握手(建立TCP连接) 四次挥手(断开连接)  4.滑动窗口 5.流量控制 6.拥塞控制 7.延迟应答 8.捎带应答  9.基于字节流 10.异常情况的处理 小结  前言 在前面,我们已经讲解了有关UDP协议的相关知识,但是在传输层,还有

linux下TCP/IP实现简单聊天程序

可以在同一台电脑上运行,在一个终端上运行服务器端,在一个终端上运行客户端。 服务器端的IP地址要和本地的IP相同,并分配端口号,客户端的默认设置为本地,端口号自动分配。 服务器端: #include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <sys/types.

JAVAEE初阶第七节(中)——物理原理与TCP_IP

系列文章目录 JAVAEE初阶第七节(中)——物理原理与TCP_IP 文章目录 系列文章目录JAVAEE初阶第七节(中)——物理原理与TCP_IP 一.应用层重点协议)1. DNS2 .NAT3. NAT IP转换过程 4 .NAPT5. NAT技术的缺陷6. HTTP/HTTPS7. 自定义协议 二. 传输层重点协议 1 .UDP协议 2.1.1 UDP协议端格式 2.1.2 UD

深入理解TCP通信

这大概是自己博客上面第三次写TCP通信demo了,总是写同样的内容也不太好啊,不过每一次都比前一次进步一点。这次主要使用了VIM编辑工具、gdb调试、wireshirk、netstat查看网络状态。 参考《C++服务器视频教程》、《Unix网络编程》 一、VIM常用命令 vim server.cpp #打开一个文件:w 写入文件:wq 保存并退出:q! 不保存退出显示行号

浏览器工作原理(3)-TCP协议文件如何从服务器到浏览器

浏览器工作原理-TCP协议,文件如何从服务器到浏览器 本周继续学习浏览器工作原理及实践,本次内容来看一下TCP协议确保文件完整的送到至浏览器 First Page 是指页面加载到首次开始绘制的时长,而影响这个性能指标的一个重要原因是网络加载速度,网络传输协议无论使用http还是websocket,都是基于TCP/IP的,所以有必要了解一下TCP/IP,对于web的性能调优和问题定位都有很

应用层简单实现udp / tcp网络通信

一、常见网络接口总结 1、创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) int socket(int domain, int type, int protocol); domain:AF_INET:网络通信,AF_LOCAL:本地通信 type:UDP:SOCK_DGRAM,TCP:SOCK_STREAM protocol:协议编号一开始设0 返回值:文件描