网络:TCP的重传机制

在这里插入图片描述

什么是重传机制

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

在TCP中,当发送端的数据到达接收端之后,接收端主机会返回一个确认应答消息,表示已经接收到消息
在这里插入图片描述
在网络复杂,并不一定能够上上图那样正常的数据传输,数据有可能在传输过程中丢失。所以TCP针对数据包丢失测情况,会用重传机制来解决

常见重传机制

有四种:

  • 超时重传
  • 快速重传
  • SACK
  • D-SACK

超时重传

重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间RTO后,没有收到对方的ACK确认应答报文,就会重发该数据,也就是超时重传

什么时候会超时重传

TCP会在下面两种情况中发生超时重传:

  • 发送的数据没能到达接收端,所以对方没有响应
  • 接收端接收到数据,但是ACK报文在返回的过程中丢失
  • 接收端拒绝或者丢弃数据

在这里插入图片描述

RTT和RTO

在这里插入图片描述

  • RTO(Retransmission Timeout 超时重传时间):
    • 从上一次发送数据,因为长期没有收到ACK响应,到下一次重发之间的时间。就是重传时间
    • 通常每次重传RTO是前⼀次重传间隔的两倍,计量单位通常是RTT。例:1RTT,2RTT,4RTT,8RTT…。重传次数到达上限之后停⽌重传。
  • RTT(Round-Trip Time 往返时延)就是数据从网络一端传输到另一端所需的时间,也就是包的往返时间
    • 大小不稳定。目的是接收方通过TCP头窗口字段告知发送方本方可以接收的最大数据量,用以解决发送速率过快导致接收放不能接收的问题。

假设在重传的情况下,超时时间 RTO 「较长或较短」时,会发生什么事情呢?

  • 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

在这里插入图片描述
精确的测量超时时间 RTO 的值是非常重要的,这可让我们的重传机制更高效。

根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

在这里插入图片描述
至此,可能大家觉得超时重传时间 RTO 的值计算,也不是很复杂嘛。

好像就是在发送端发包时记下 t0 ,然后接收端再把这个 ack 回来时再记一个 t1,于是 RTT = t1 – t0。没那么简单,这只是一个采样,不能代表普遍情况

实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值

我们来看看 Linux 是如何计算 RTO 的呢?

估计往返时间,通常需要采样以下两个:

  • 需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。
  • 除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况。

RFC6289 建议使用以下的公式计算 RTO:
在这里插入图片描述
其中 SRTT 是计算平滑的RTT ,DevRTR 是计算平滑的RTT 与 最新 RTT 的差距。

在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。别问怎么来的,问就是大量实验中调出来的。

如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP的策略是超时间隔加倍

也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设置为先前值的两倍。两次超时,就说明网络环境差,不易频繁反复发送

超出触发重传的问题是,超时周期可能比较长。那是不是有更快的方式呢?

于是可以用【快速重传】机制解决超时重发的时间等待

快速重传

TCP 还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传

快速重传机制,是如何工作的呢?其实很简单,一图胜千言。

在这里插入图片描述

在上图,发送方发出了 1,2,3,4,5 份数据:

  • 第一份 Seq1 先送到了,于是就 Ack 回 2;
  • 结果 Seq2 因为某些原因没收到,Seq3 到达了,于是还是 Ack 回 2;
  • 后面的 Seq4 和 Seq5 都到了,但还是 Ack 回 2,因为 Seq2 还是没有收到;
  • 发送端收到了三个 Ack = 2 的确认,知道了 Seq2 还没有收到,就会在定时器过期之前,重传丢失的 Seq2
  • 最后,收到了 Seq2,此时因为 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。

所以,快速重传的工作方式是当收到三个相同的ACK报文时,会在定时器过期之前,重传丢失的报文段。

快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另一个问题,就是重传的时候,是重传之前的一个,还是重传所有的问题

比如对于上面的例子,是重传 Seq2 呢?还是重传 Seq2、Seq3、Seq4、Seq5 呢?因为发送端并不清楚这连续的三个ACK2是谁传回来的

为了解决不知道该重传哪些TCP报文,于是有了SACK方法

SACK方法

SACK( Selective Acknowledgment 选择性确认)。它会在TCP头部的[选项]字段里面加一个SACK的东西,它可以将缓存的地图发送给发送方,这样发送方就可以知道哪些数据送到了,哪些数据没有收到,知道了这些信息,就可以只重传丢失的数据

如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。
在这里插入图片描述

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

Duplicate SACK

Duplicate SACK 又称 D-SACK,其主要使用SACK来告诉[发送方]有哪些数据被重复接收了

下面举例两个栗子,来说明 D-SACK 的作用。
在这里插入图片描述

  • 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499)
  • 于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」 3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着 D-SACK。
  • 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。

在这里插入图片描述

  • 数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
  • 而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」;
  • 所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。
  • 这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。

可见,D-SACK 有这么几个好处:

  • 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
  • 可以知道是不是「发送方」的数据包被网络延迟了;
  • 可以知道网络中是不是把「发送方」的数据包给复制了;

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

小结

如上图:
-在发送端来看:1、2、3已经发送并确认;4、5、6、7、8、9都是发送了还没有确认;10、11、12是还没有发出的;13、14、15是接收方没有空间,不准备发的

  • 从接收端来看:1、2、3、4、5是已经完成ACK,但是没有读取的;6、7是等待接收的;8,9是已经接收但是没有ACK的

也就是说:

  • 1、2、3没有问题,双方达成了一致
  • 4、5接收方说ACK了,但是发送方还没有收到,有可能丢了,有可能在路上
  • 6、7、8、9肯定是都发了,但是8、9已经到了,6、还没有到,出现了乱序,缓存这但是没办法ACK

假设4的ACK到了,不幸的是,5的ACK丢了,6、7的数据包丢了,该怎么办呢?

  • 一种方法是超时重试,也就是对每一个发送了,但是没有ACK的包,都有设一个定时器,超过了一定的时间,就重新尝试。
    • 但是这个超时时间如何评估呢?这个时间不宜过短,时间必须大于往返RTT,否则会引起不必要的重传;也不易过长,这样超时时间变长,访问就变慢了
    • 估计往返时间,需要TCP通过采样RTT的时间,然后进行加权平均,算出一个值,而且这个值还是要不断变化的,因为网络状态会不断变化。除了采样RTT,还有采样RTT的波动范围,计算出一个估计的超时时间。由于重传时间是不断变化的,这个较少自适应重传算法
    • 如果过一段时间,5、6、7都超时了,就会重新发送。接收方发现5原来接收过,于是丢弃5;6收到了,发送ACK,要求下一个是7,7不幸又挂了。当7再次超时的时候,有需要重传的时候,TCP的策略是超时间隔加倍,每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不易频繁反复发送
    • 超时触发重传存在的问题是,超时周期可能想对比较长,那是不是有更快的方式?
    • 于是就有了快速重传机制:当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间隔,于是发送三个冗余的ACK。对方受到后,就在定时器过期之前,重传丢失的报文段
    • 比如,接收方发现6、8、9都已经收到了,就是7没来,那肯定是丢了,于是发送三个6的ACK,要求下一个是7。对方收到3个ACK,就会发现7的确是丢了,于是不等超时,马上重发
  • 另一个方法是selective acknowledge(SACK)。这种方式需要在TCP头里面加一个SACK的东西,可以将缓存的地图发送给接收方。比如可以发送ACK6、SACK8、SACK9,有了地图,发送方一下子就能看出来是7丢了
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值