1,重传基本原理
TCP协议是一种面向连接的可靠的传输层协议,它保证了数据的可靠传输。既然是可靠的传输,那对于丢包情况肯定有一套重传的机制。
TCP重传的基本原理:在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到发送数据的ACK确认报文,则对该报文进行重传。
上面的时序图,就是TCP重传的全部内容吗?好像很简单啊。
答案是:当然不是。
我们考虑一下下面这些问题:
1)RTO的时间是怎么来的?
2)每发一个包,都会启动一个定时器吗?那会不会导致定时器特别多,对系统性能有影响?
3)每个丢包都需要RTO超时之后,才能重传吗?对于偶尔丢了一个包,后面包都收到的场景,是否可以快速检测到该场景,并快速重传?
4)前面一个包丢了,后面所有的包都需要重传,即使已经发送成功;是否可以做到只重传已丢包的包,对于已收到的包不需要重传?
下面我们就来深入的讨论TCP重传机制的细节和原理,解决上面提到的问题。
2,RTO计算方法
RTO:英文全称是Retransmission TimeOut,即重传超时时间;
RTO是一个动态值,会根据网络的改变而改变。RTO是根据给定连接的往返时间(RTT,全称:Round Trip Time)的测量值而计算出来的。
那么RTT又是如何测量得到的呢?
有有种测量方法:
1)重传队列中数据包的TCP控制块
TCP每发送一个数据包,就会把该数据包复制一份放到TCP重传队列中,数据包skb中的TCP控制块包含着一个变量tcp_skb_cb->when,记录了该数据包的第一次发送时间;收到该数据包的ACK时,再根据:当前时间 - when,得到RTT时间;
2)开启TCP Timestamp选项
需要开启tcp_timestamps选项,可以通过sysctl命令修改和查看;
当接收端和发送端同时支持TCP时戳选项时,发送端记录在TCP包头选项内的时戳可以被接收端随响应反射回来,发送端就可以利用响应报文的反射时戳计算出某个TCP包的即时往返传输时间。
RTT = 当前时间 - 数据包中Timestamp选项的回显时间 这个回显时间是该数据包发出去的时间,知道了数据包的接收时间(当前时间)和发送时间(回显时间),就可以轻松的得到RTT的一个测量值。
既然不用TCP Timestamp选项就能测量出RTT,为什么还要多此一举?
因为根据TCP Timestamp测出来的RTT更加准确;对于重传的数据包的响应,重传队列方法并不知道重传的开始时间,所以没办法采集起来作为一个样本;而TCP Timestamp方法则可以。
根据RTT计算RTO的方法:
RTO = srtt >> 3 + rttvar
RTO计算时,使用了一次指数平滑算法。
srtt为经过平滑后的RTT值,它代表着当前的RTT值,每收到一个ACK更新一次。 为了避免浮点运算,它是实际RTT值的8倍。 mdev为RTT的平均偏差,用来衡量RTT的抖动,每收到一个ACK更新一次。 mdev_max为上一个RTT内的最大mdev,代表上个RTT内时延的波动情况,有效期为一个RTT。 rttvar为mdev_max的平滑值,可升可降,代表着连接的抖动情况,在连接断开前都有效
3,超时定时器的实现
具体的TCP协议实现中,并不是每一个包都有一个定时器;如果每一个包都有一个定时器,那系统中定时器数据就太多了,会消耗很多资源,性能会比较差;
所以,TCP的实现中,都是一个连接对应一个超时定时器;
那一个连接对应一个定时器,那一个数据包的超时时间严格等于RTO时间吗?答应是:一个连接只有一个超时定时器,那么对于每个数据包,没办法做到超时时间严格等于RTO;但是可以保证超时时间不大于2*RTO。其实这就是性能和准确性的权衡考虑。
那TCP的超时定时器具体是怎实现的呢?
原则:
1.每一个报文在长期收不到确认都必须可以超时;
2.这个长期收不到中长期不能和测量的RTT相隔太远;
实现方法:
a.发送TCP分段时,如果还没有重传定时器开启,那么开启它。
b.发送TCP分段时,如果已经有重传定时器开启,不再开启它。
c.收到一个非冗余ACK时,如果有数据在传输中,重新开启重传定时器。
d.收到一个非冗余ACK时,如果没有数据在传输中,则关闭重传定时器。
根据a和c(在c中,注意到ACK是非冗余的),任何TCP分段只要不被确认,超时定时器总会超时的。然而为何需要c呢?只有规则a存在的话,也可以做到原则1。实际上确实是这样的,但是为了不会出现过早重传,才添加了规则c,如果没有规则c,那么万一在重传定时器到期前,发送了一些数据,这样在定时器到期后,除了很早发送的数据能收到ACK外,其它稍晚些发送的数据的ACK都将不会到来,因此这些数据都将被重传。
4,快速重传
因为RTO超时重传的代价是比较大,会导致拥塞控制机制进行慢启动过程。对于因为网络毛刺或者随机因素导致的偶尔单个丢包,如果也进行RTO超时重传,会影响网络传输的性能。
对于这种场景,引入了快速重传机制。
发送方连续收到3次相同的ack,这个时候即使超时定时器还没有超时,也开始启动重传。
5,选择性重传
TCP通信时,如果发送序列中间某个数据包丢失,TCP会通过重传最后确认的包开始的后续包,这样原先已经正确传输的包也可能重复发送,急剧降低了TCP性能。为改善这种情况,发展出SACK(Selective Acknowledgment, 选择性确认)技术,使TCP只重新发送丢失的包,不用发送后续所有的包,而且提供相应机制使接收方能告诉发送方哪些数据丢失,哪些数据重发了,哪些数据已经提前收到等。
1)该功能可配置,通过系统参数:net.ipv4.tcp_sack配置;使用sysctl命令修改和查看。
2)CP头里加一个SACK选项,说明了接收到的数据的区间。
3)存在接收方Reneging情况,所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了。这样干是不被鼓励的,因为这个事会把问题复杂化了,但是,接收方这么做可能会有些极端情况,比如要把内存给别的更重要的东西。所以,发送方也不能完全依赖SACK,还是要依赖ACK,并维护Time-Out,如果后续的ACK没有增长,那么还是要把SACK的东西重传,另外,接收端这边永远不能把SACK的包标记为Ack。
4)SACK会消费发送方的资源,试想,如果一个攻击者给数据发送方发一堆SACK的选项,这会导致发送方开始要重传甚至遍历已经发出的数据,这会消耗很多发送端的资源。
6,DSACK
DSACK是在SACK的基础上做了一些扩展,主要用于对收到的重复报文进行了处理。 DSACK同样使用了与SACK一样的报文格式,唯一区别在于:第一个连续的block指定的是触发DSACK的重复报文的序号空间。
DSACK主要作用是:告诉发送方有哪些数据被重复接收了。
区分SACK和D-SACK的方法:
D-SACK使用了SACK的第一个段来做标志,
1)如果SACK的第一个段的范围被ACK所覆盖,那么就是D-SACK
2)如果SACK的第一个段的范围被SACK的第二个段覆盖,那么就是D-SACK
引入了D-SACK,有这么几个好处:
1)可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢了。
2)是不是自己的timeout太小了,导致重传。
3)网络上出现了先发的包后到的情况(又称reordering)
4)网络上是不是把我的数据包给复制了。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。