面试官:TCP协议的重传机制了解过吗?来讲一讲!


重传机制


TCP实现可靠传输的方式之一:通过序列号和确认应答。

接收端收到发送端发送的消息,会返回一个确认应答告知接收端已收到数据 ACK=已收到数据的最后序号+1,告知发送端下次要发送的数据的序号。

但是如果数据在网络传输的过程中丢失呢?TCP针对数据包丢失的情况,设计了重传机制,四种常见的重传机制如下:

  1. 超时重传
  2. 快速重传
  3. SACK
  4. D-SACK


(1)超时重传


发送端在发送数据时会设定一个定时器,当定时器超时后依旧没有收到对方的ACK确认应答报文,就会重传原数据包,这就是所谓的超时重传。

有如下两种情况会发送超时重传:

  1. 确认应答报文丢失
  2. 接受方接收到了数据,但是其发送的ACK报文在网络中丢失了,发送方一直收不到ACK,定时器到时就会触发超时重传机制。
  3. 数据包丢失
  4. 发送方发送的报文在网络中丢失,接受方根本没收到数据就不可能发送ACK报文,发送方一直没收到ACK报文,会触发超时重传机制。

那么超时重传时间应该设置为多少呢?

先说结论:超时重传时间 RTO 应略大于报文往返时间 RTT 的值。

  1. RTO:Retransmission Timeout 超时重传时间。
  2. RTT:Round-Trip Time 往返时延,指的是数据发送时刻到收到ACK的时刻的差值,即包的往返时间。

为什么 RTO 应略大于 RTT呢?

  1. 当 RTO 较大时,包丢了很久才重发,重发很慢,效率低,性能差。
  2. 当 RTO 较小时,重发很快,有可能包没丢就重发包,是不必要的重传,会导致网络压力增大。

所以 RTO 的值可以决定 TCP 的超时重发机制是否高效,最合适的值就是略大于 RTT,就是比包的往返时间略大一些,这样包丢了也不会等很久才重传,也不会包没丢就很快地重传从而增加了网络负荷。

因为网络是变化的,RTT也就是经常变化的,所以 RTO 应该是一个动态变化的值

Linux 中是如何计算 RTO 呢?

  1. 采样 RTT, 进行加权平均从而算出一个平滑 RTT 的值,并且因为网络是变化的,这个值也要不断变化
  2. 采样 RTT 的波动范围,避免如果 RTT 出现一个较大波动时很难被发现的情况。

计算RTO的公式,简单看一看:

其中 SRTT 是计算平滑的RTT ,DevRTR 是计算平滑的RTT 与 最新 RTT 的差距。

在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4,大量实验得出来的。

如果触发超时重传而重发的而数据再次超时时,TCP的超时重传时间会翻倍,理由就是:超时了两次说明网络环境差,不宜频繁发送重复数据,要降低重传频率。

但是超时重传时间可以会越来越长,出现超时重复的等待时间很长的问题,使传输效率降低,这时就需要快速重传机制登场了。


(2)快速重传


快速重传(Fast Retransmit)机制,不以时间为驱动,而是以数据驱动重传,其工作方式为:

  1. 当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

快速重传机制解决了超时时间过长的问题,它依然面临着另外一个问题:重传的时候,重传一个还是重传所有?

  1. 假设一个场景,发送方发送了6个数据,编号1~6,2和3丢失,那么接收方再收到4、5、6时,都是回复ACK2给发送方,发送方自然不清楚连续3个ACK是接受方收到哪个报文而回复的,那么
  2. 如果发送方只重传2一个报文,之后对于3报文来说,还得在之后收到三个重复的ACK3才能触发重传,重传的效率很低。
  3. 如果发送方重传2及其之后已发送的所有报文(2、3、4、5、6),虽然丢失的2、3报文能被成功重传,但是对于接收方以及接收的4、5、6报文,等于做了无用功,比较浪费资源

所以只要发送方不知道接收方已经接收了哪些报文,不管发送方选择重传一个还是重传所有已发送的报文,都存在效率低或浪费资源的问题,这时就轮到 SACK 方法登场了。


(3)SACK


SACK(Selective Acknowledgement 选择性确认),在 TCP 头部 选项 字段加一个 SACK,它乐于将已收到的数据的信息发送给发送方,这样发送方就可以直到接收方哪些数据收到了,哪些数据没收到,就可以只重传没收到的数据,即丢失的数据。

可以看如下图理解这个过程:

当接收端收到三次重复的ACK200,会根据SACK中的信息得知目前300-599的数据已经接收了,只要重发200-299的数据即可。

如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开),现在基本上都是支持 SACK 的。


(4)D-SACK


D-SACK(Deplicate SACK),使用 SACK 来告诉发送方有哪些数据被接收方重复接收了,那么 D-SACK 有哪些作用呢?

  1. 当发送ACK丢失时,接收方确实接收到了数据,只是ACK报文丢失了,接收方会重传数据,发送方再次发送ACK报文时 ACK > SACK 范围,则说明重传的数据接收方之前已经接收过了,发送方就知道了发送的数据并没有丢失,而是应答报文丢失了。
  2. 当发生网络延时,某个数据包被网络延迟了导致发送方没收到ACK确认报文,发送方继续发送报文,当收到后面报文到达的三个相同的ACK报文时,触发快速重传机制,但是在重传过程中被延迟的报文可能正好达到了接收方,接收方回复的ACK>SACK,代表收到了重复的数据,SACK的范围是重复接收到的数据包的范围,这样发送方就知道快速重传的触发不是因为ACK报文或者发出去的数据包丢失了,而是因为网络延迟。

所以 D-SACK 有如下三个好处

  1. 发送方可以知道是发出去的包丢了还是接收方回应的ACK包丢了
  2. 可以知道发送方发出的包是不是被网络延迟了
  3. 可以知道发送方发出的包是不是因网络中的某些原因被复制了

在 Linux 下通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。


总结


因为网络不稳定存在丢包的情况,从而有了超时重传机制,因为重传等待时间过长的缘故从而有了快速重传机制,因为快速重传机制无法让发送方知道哪些包是接收方已经接收到,存在传输效率低、浪费资源等问题,从而有了SACK,又有了D-SACK可以告知接收方那些包被重复接收了,从而可以判断ACK包丢失、网络延迟等多种情况。

参考资料:小林图解计网、jk时间趣谈网络协议


0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
CarreyWu
下载 APP