TCP 重传机制/滑动窗口/流量控制/拥塞控制

TCP 重传机制、滑动窗口、流量控制、拥塞控制

重传机制

超时重传

发送完一个 TCP 报文后,发送方会启动重传计时器

若在超时时间内,都没有收到 ack,就会触发超时重传,以保证可靠传输

重传时间 RTO 的确定很重要:

  • 如果过小,会造成不必要的重传,浪费网络资源
  • 如果过大,会造成报文整体传输时间过长,增加延迟

那该如何确定 RTO 的值?

一般来说,RTO 设置为 RTT 再加上一小段时间比较合适

这里的 RTT 是加权平均往返时间

此外,如果重传后,仍未收到 ack,那么下一次重传的时间是上一次的 2 倍

快重传

快重传是指:不用等待重传计时器超时,只要收到三次重复的 ack,就重传

在这里插入图片描述

图片来自小林 coding

那么问题来了,重传哪些报文呢?

假设发送了报文 1 ~ 6,其中,2、3 丢失了,发送方收到三个重复的 ack = 2

  • 如果重传所有的报文,显得没必要,浪费资源,因为对方已经收到部分报文了
  • 如果仅重传 2,那么报文 3 又要等到收到三个 ack 才能重传,效率低

SACK

SACK 就是用来解决上面的问题的

在这里插入图片描述

图片来自小林 coding

SACK 包含了已经收到的报文的序列号,这样,发送方就可以只重传丢失的报文了

滑动窗口

为什么有滑动窗口?

假设没有滑动窗口,使用 TCP 发送一条数据后,必须要等待对方发来 ack 以后,才能发送下一条数据,效率很低

如果引入「窗口」的概念,发送方发送的字节数小于窗口大小时,无需等待对方发来 ack,而是继续发送,直到未确认的字节数大小等于发送窗口大小,大大提高效率

此外,窗口内发送的多条数据,不一定每一个都要收到 ack(即允许部分 ack 丢失,或者压根就不确认),只要收到最后一个报文的 ack,就可以一并确认之前的报文,这种机制叫做 累计确认,进一步提高了效率

窗口大小由谁确定?

窗口大小通常由 接收方 确定

这是因为,接收方需要处理发送方发来的数据,发送方不应该发送太快,以至于接收方来不及处理

因此,接收方可以根据处理能力,动态调整窗口的大小,控制发送方的发送速率

发送窗口

在这里插入图片描述

接收窗口

在这里插入图片描述

二者大小是否相等?

二者的大小可以说是大约相等

接收方动态更新自己的接收窗口大小,并通告给发送方,这个过程存在延迟

流量控制

基于滑动窗口的流量控制过程

在这里插入图片描述

OS 对滑动窗口的控制

真实的流量控制过程不会像上面一样那么简单(上面的接收窗口大小始终没有变化)

接收方可能比较忙,虽然接收了数据,但处理数据的能力比较弱

在这种情况下,buffer 中会堆积较多的数据,为了防止将 buffer 打满,OS 会动态调整接收窗口的大小

在这里插入图片描述

图片来自小林 coding

上面的情况比较极端,最后的接收窗口大小甚至减为 0!

当应用将堆积的数据取走以后,就可以恢复窗口的大小,向发送方通告此时的接收窗口大小,让发送方可以继续发送数据

那么,如果通告消息丢失了呢?

接收方等待发送方发送数据,而发送方也在等待接收方通告窗口大小,造成死锁?!

零窗口探测报文

事实上,上面的「死锁」是不会发生的

当发送方收到的接收方的窗口大小为 0 时,会启动持续计时器

当持续计时器超时后,发送方就会向接收方发出 零窗口探测报文

  • 如果窗口大小确实还为 0,重置计时器
  • 如果窗口大小不为 0,重置发送窗口大小,并继续发送数据
  • 如果超时未收到响应,触发重传

窗口的探测次数一般为 3,探测超过 3 次,窗口还为 0(或者没有响应),有的 TCP 实现就会发出 RST 报文,终止连接

糊涂窗口综合征

糊涂窗口综合征是指:接收方的窗口太小(或设置不当),导致发送方每次发送的包很小,进而提高发送次数,以及 ack 次数,造成的网络拥塞现象

分析一波,问题主要出现在:

  • 接收方的窗口太小
  • 发送方每次发送的数据太少

解决第一个问题,接收方的通常处理策略为:

  • 当接收窗口大小小于 min(MSS, cache_size / 2) 时,发送零窗口报文给对方,让对方不再发送数据
  • 等待应用逐渐取走数据,直到接收窗口大小 >= min(MSS, cache_size / 2),发送当前的窗口大小

解决第二个问题,发送方的通常处理策略为:

使用 Nagle 算法

  • 当窗口大小 >= MSS,并且待发送数据大小 >= MSS
  • 收到一个 ack

只要满足其中一个条件,就可以发送数据

可以发现,使用 Nagle 算法,接收方必须保证不通告小窗口给发送方,否则满足第二个条件,还是可以发送小包,造成糊涂窗口综合征

此外,由于 Nagle 算法默认启用,对于传递的数据本身就比较小的场景(如 ssh),需要禁用 Nagle 算法,以保证低延迟

拥塞控制

什么是拥塞窗口?

拥塞窗口 cwnd 的大小为 拥塞窗口和接收窗口二者的最小值,由发送方维护

前面提到的流量控制,控制的是单个 TCP 连接的流量,但对整个网络而言,是无感的

TCP 是一个「无私」的协议,当它检测到网络出现了拥塞,就会减缓自己连接的速率,避免整个网络的拥塞程度加深

如何判断是否出现拥塞?

当重传计时器超时,就可以认为,网络出现了拥塞

拥塞控制全过程

整体由四个算法构成:

  • 慢开始
  • 拥塞避免
  • 快重传
  • 快恢复

在这里插入图片描述

  • 首先执行慢开始,cwnd 指数增加,直到到达 ssthresh(门限值)
  • 到达 ssthresh 后,开始拥塞避免,cwnd 线性增加
  • 如果发生超时重传,我们认为发生拥塞,重传后,cwnd 设为初始值,ssthresh 设置为 ssthresh / 2,重新执行慢开始
  • 如果收到三个重复 ack(认为丢包),快速重传,执行快恢复(ssthresh 设置为 ssthresh / 2,cwnd 设置为 当前的 ssthresh),然后执行拥塞避免

详细说说快恢复

快恢复开始于收到 3 个重复的 ack 时

第一种实现,就像上一张图展示的一样,快重传后,ssthresh 设置为 ssthresh / 2,cwnd 设置为 当前的 ssthresh

第二种实现有所不同:

在这里插入图片描述

图片来自小林 coding

sthresh 设置为 ssthresh / 2,执行快恢复,cwnd 设置为 当前的 ssthresh + 3

  • 如果再收到相同的 ack,cwnd + 1
  • 否则,将 cwnd 设置为当前的 sthresh,开始拥塞避免

为什么快恢复将 cwnd 设置为 当前的 ssthresh + 3

加 3 代表快速重传时已经确认接收到了 3 个重复的数据包;

为什么快恢复过程中还要增加 cwnd?

过程 2(cwnd 逐渐加 1)的存在是为了尽快将丢失的数据包发给目标,从而解决拥塞的根本问题(三次相同的 ACK 导致的快速重传),所以这一过程中 cwnd 反而是逐渐增大的。

最终的 cwnd 还是减小的(减为之前的 sthresh 的一半)

为什么有了流量控制还需要拥塞控制?

前面提到的流量控制,控制的是 单个 TCP 连接的流量,但对整个网络而言,是无感的

TCP 是一个「无私」的协议,当它检测到网络出现了拥塞,就会减缓自己连接的速率,避免整个网络的拥塞程度加深

如果没有拥塞控制,并且发生了拥塞,那么整个网络的可用性下降,丢包率上升,需要更多的重传,更多的重传又会进一步加深拥塞程度,是一个 恶性循环

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值