4.2 TCP 重传、滑动窗口、流量控制、拥塞控制 | 小林coding (xiaolincoding.com)
重传机制
TCP 实现可靠传输的方式之一,是通过序列号与确认应答。
一旦数据丢失就要重传:
超时重传
重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。
超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。
快速重连
快速重传(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 报文时,会在定时器过期之前,重传丢失的报文段。
SACK- 解决上面两个传输同时丢失的情况
- 这种方式需要在 TCP 头部「选项」字段里加一个
SACK的东西,它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
D-SACK
- 使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
- ACK丢失,发送方以为没发成功就一直发,超时重发,但是返回的ack为4000,意味着丢包
滑动窗口 -- 避免相应等待
窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。
流量控制 -- 不能无脑发送
让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
由滑动窗口协议(连续ARQ协议)实现。
例子:
在前面我们都看到了,TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。
窗口关闭
如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
但如果窗口关闭后,接受方处理完数据窗口增大了,于是想通知发送方我的窗口变大了,但是这个ACK报文丢失,那么就会进入死锁状态。
为了解决死锁呢,tcp为每个连接设置一个持续定时器,只要tcp连接一方收到0窗口通知,就打开持续计时器,如果超时,就发送窗口探测报文,对方收到会给出自己现在窗口的大小。如果几次探测之后还是0,那么tcp会中断连接
糊涂窗口综合症
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
接收方通常的策略如下:
当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。
等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
发送方通常的策略:
使用 Nagle 算法,该算法的思路是延时处理,它满足以下两个条件中的一条才可以发送数据:
- 要等到窗口大小 >=
MSS或是 数据大小 >=MSS - 收到之前发送数据的
ack回包
拥塞控制
- 拥塞控制针对网络,不针对接收方。避免「发送方」的数据填满整个网络。
- 发送窗口的值是swnd = min(cwnd, rwnd),
拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。
慢启动
这个慢启动的意思就是一点一点的提高发送数据包的数量,如果一上来就发大量的数据,这不是给网络添堵吗?当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。
设置一个阈值当cwnd<阈值的时候,执行慢启动 ,当cwnd>阈值的时候执行拥塞避免算法
拥塞避免算法
每当收到一个 ACK 时,cwnd 增加 1/cwnd。
但是一直增加也会发生拥塞丢包,需要重传,进入拥塞发生算法」。
拥塞发生算法
快速恢复
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
正如前面所说,进入快速恢复之前,cwnd 和 ssthresh 已被更新了:
cwnd = cwnd/2,也就是设置为原来的一半;ssthresh = cwnd;
然后,进入快速恢复算法如下:
- 拥塞窗口
cwnd = ssthresh + 3( 3 的意思是确认有 3 个数据包被收到了); - 重传丢失的数据包;
- 如果再收到重复的 ACK,那么 cwnd 增加 1;
- 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;