[原]【原创】TCP超时重传机制探索
TCP超时重传机制探索
作者:tll (360电商技术)
1)通信模型
TCP(Transmission Control Protocol)是一种可靠传输协议,在传输过程中当发送方(sender)向接收方(receiver)发送的数据丢失时,将引起发送方向接收方重传丢失的数据包。其通信模型如下:
图1
2)超时重传
重传机制在实现数据可靠传输功能的同时,也引起了相应的性能问题:何时进行数据重传?如何保证较高的传输效率?
重传时间过短:在网络因为拥塞引起丢包时,频繁的重传会进一步加剧网络拥塞,引起丢包,恶化网络传输性能。
重传时间过长:接收方长时间无法完成数据接收,引起长时间占用连接线路造成资源损耗、传输效率较低等问题。
针对上述问题,TCP中设计了超时重传机制。该机制规定当发送方A向B发送数据包P1时,开启一个时长为RTO(Retransmission Timeout)的重传定时器,如果A在RTO内未收到B对P1的确认报文,则认为P1在网络中丢失,此时重新发送P1。由此,引出RTO大小的设定问题。
对RTO大小的设定,由于的传输特性不同将导致发送与接收方完成一次数据传输(发送与应答)的时间不同(例:通常有线网络的传输速率远高于无线网络),因此固定大小的RTO无法满足不同环境下的传输需求,由此TCP采用针对具体传输环境动态测量的方式来确定当前时刻的RTO.
针对上述问题TCP中引入RTT(Round Trip Time),其中RTT为一个数据包从发出去到收到对该包确认的时间差,并根据RTT计算RTO.
由于RTT为单次测量的结果,常常受到网络迟延等因素的影响,波动较大,便记录多次的RTT值,通过加权移动的方式计算出一个更加合理的数值即SRTT(Smoothed RTT),根据SRTT计算RTO.由此一次RTO的计算通过如下过程得出:
RTT---->SRTT---->RTO
对于上述数值的计算 RFC793做了相应描述,具体公式如下
SRTT = (α* SRTT ) + ((1-α) * RTT)
RTO = min[UBOUND, max[LBOUND, (β*SRTT)]]
其中 UBOUND 为timeout的上限 (e.g., 1 minute),
LBOUND 为timeout的下限 (e.g., 1 second),
α为平滑因子 (e.g., .8 to .9),
β为时延变化因子 (e.g., 1.3 to 2.0).
由上式可以看出RTO的计算相对保守,每次新到的RTT对RTO影响较少,如此将导致RTO在RTT变化较大的环境中不能做及时调整,更好的适应当前环境。如此,Van jacobson在 Congestion Avoidance and Control 一文中对RTO的计算提出了一种快速算法
其中A为SRTT,M为最新RTT,D为偏差,g=1/2^n ,进一步
如此求得A与D,而RTO=A+4D,完成对RTO计算。
Linux中具体实现参考 tcp_rtt_estimator
其中而在Linux中RTO的最小值为200ms
#define TCP_RTO_MIN((unsigned)(HZ/5)) /* 200ms */
3)快速重传
TCP继续发展,重传机制得到进一步加强,出现了快速重传。如下图所示:
图2
快速重传机制规定:发生丢包时,在重传定时器被触发之前,当发送方连续收到3个对丢失数据包的重传请求时将引起立即重传。
如上图所示,数据包2在发送过程中丢失,然而在此时的重传定时器未到达之前,发送方连续收到4个ACK,其中第一个为对数据包1的确认,后3个ACK在对收到的数据包4,5,6做选择性应答的同时,连续3次告知对方缺少对数据包2的接收,由此触发快速重传,数据包10为对丢失数据包2的重传。
此时如果超时计时未到,将使得发送方能更加及时的发送出待重传数据包;如果超时计时到达,则同样立即发送待重传数据包。
由此可看出,快速重传机制在一定程度上弥补了超时重传机制,使得重传更加及时。
4)选择性重传
由于网络传输路径往往是复杂的,数据包在传输中可能发生丢包或者乱序到达,而TCP只对连续的最后一个序列号做应答,使得乱序提前到达的数据包被重传,导致传输性能下降。
为改善此现状,RFC2018中提出了SACK(Selective Acknowledgement)。通过对ACK选项中添加已经接收的不连续数据块信息,通过对方不必重复发送对方已经接收的乱序数据块。
其中TCP首部中SACK选项如下所示:
图3
选项中分别用Left Edge of Block 与 Right Edge of Block来表示一个不连续数据块的第一个数据序号和最后一个数据序号+1,由此Right Edge of Block(n)与Left Edge of Block(n-1) – 1 的间隔表示丢失数据段。
图4
如上图所示,发送方在发送报文段5,6丢失的情况下,接收方应答报文9完成对序列号201之前数据的应答,同时以SACK 301-401 501-601告知对方已接收数据段。发送方此时只需重传丢失数据段201-300以及401-500之间的数据即可。
选择重传的出现,在一定程度上大大降低了网络重传的数量,提高了网络吞吐量。
5)定时器的开启
图5
a. 重传计时器对哪些数据包进行计数?
TCP对每一个连接采用一个重传计时器,当定时器已被使用时,当前发送的数据段不被计时。
如上图所示,由于发送数据段3时启动计时器,所以数据段4在计时器结束之前不再计时。
b. 重传计时器的起始时间?
如图发送数据段3,开启计时器,同时记录此时发送数据包的SEQ(257),在接收到含有SEQ的ACK时,完成计时。如确认段5对SEQ 513之前数据的确认,包含RTT#2 计时起始的SEQ(257),此时RTT#2完成计时。
同理RTT#3中开启计时时SEQ(769)此时确认段8中是对SEQ在769之前的确认(不包含769),所以此时RTT3并不结束。
6)重传次数与时间
图2描述了网络中生成一次丢包与重传的情景,然而网络的传输场景是复杂的,引起网络的多次重传,针对连续的重传定时TCP采用了指数退让的方式来降低网络负载。
图6
如上图所示,在第12数据包发生丢失时,连续重传13,14,15,16,17号数据包,其间隔时间分别为6s,12s,24s,48s,96s
1)为什么不是准确的2倍?
Linux同BSD版TCP采用500ms作为一个滴答的定时器完成定时功能。例定时6s则需要记录12次滴答即可。
如下图所示,TCP设置一个6s定时器,则在第0个滴答到来时,完成计时。由于计时开始发生在12到11的任意时间,所以此时完成计时的时间将在5.5~6s之间。
图7
2)关于默认值
如此可看出,默认重传次数为3,最高重传次数为15
3)关于参数的设置与服务的配置
a. 内核中参数的设置
proc文件中查看当前网络协议栈等内核信息
sysctl –a 显示所有系统参数
例查看ipv4一些参数的设置:
图8
通过echo完成对内核参数的动态修改,重启失效。
在/etc/sysctl.conf文件中完成对参数的修改,使用sysctl –p 使之永久生效。
b. Web server中TCP的参数设置
Apache中对网络连接的设置:
图9
c. 应用程序对TCP连接参数的设置
通过setoptsock对socket选项完成对连接参数的设置,包括拥塞算法、保活时间等参数
setoptsock---> tcp_setsockopt ---> do_tcp_setsockopt
其中do_tcp_setsockopt部分代码如下:
7)小结
TCP是一项复杂的协议,重传机制作为其保证进行可靠传输而存在,致使网络编程(TCP和UDP)是一个相对复杂的任务,可以使用默认参数进行传输,然而为了提升传输性能,往往需要针对具体的环境做相应的参数设置与细节处理。例如对MTU(Maximum Transmission Unit)的考虑,不同的网络传输环境MTU可能不同,采用同等较大的数据包传输层则会引起传输效率问题,传输数据包过大会引起分片,数据包过小带块利用率较低(其中TCP数据字节流传输,用户发送数据时在传输层受限与MSS大小完成数据段分割;UDP为数据报传输,IP报文段大小受限于链路层MTU的大小,过大的数据包将完成数据分片并带来性能损耗,所以应尽量在应用层完成对报文段大小的确认,避免传输过程中不必要的分片过程)。
对保活连接时长和重传请求次数以及拥塞控制算法的选取都与应用场景有着密切的关系。如果需要进一步提升网络性能需要对具体参数做针对性调整。
<完>
-------------------------------------------------------------------------------------
黑夜路人,一个关注开源技术、乐于学习、喜欢分享的程序员
博客: http://blog.csdn.net/heiyeshuwu
微博: http://weibo.com/heiyeluren
微信: heiyeluren2012
想获取更多IT开源技术相关信息,欢迎关注微信!
微信二维码扫描快速关注本号码: