Skip to content

拥塞控制

流量控制是避免数据填满发送方的缓冲区。

而拥塞控制是避免发送方的数据填满整个网络。

在网络拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢包等。这个时候就会重传数据,但是一旦重传就会导致网络负担变得更重。于是会导致更大的延迟以及更多的丢包。

在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环被不断地放⼤….

所以,TCP 不能忽略整个网络中发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低发送的数据流。

拥塞窗口

拥塞窗⼝ cwnd是发送⽅维护的⼀个的状态变量,它会根据⽹络的拥塞程度动态变化的。

发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系,那么由于加⼊了拥塞窗⼝的概念后,此时发送窗⼝的值是 swnd = min(cwnd, rwnd),也就是拥塞窗⼝和接收窗⼝中的最⼩值。

拥塞窗⼝ cwnd 变化的规则:

  • 只要⽹络中没有出现拥塞, cwnd 就会增⼤;
  • 但⽹络中出现了拥塞, cwnd 就减少;

拥塞控制算法

image.png

慢启动

TCP建立连接之后,一开始不要发送大量数据。先探测一下网络的拥塞程度。然后逐渐增大拥塞窗口的大小。

  • 如果没有发生丢包,每收到一个ACK,将拥塞窗口 cwnd 大小加1。
  • 每轮次发送窗口增加一倍,呈指数增长。如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。

image.png

发包的个数呈指数级增强

image.png

为了防止拥塞窗口无限增长,还需要设置一个慢启动阀值 ssthresh(slow start threshold)状态变量。

当到达该阈值后,即 cwnd >ssthresh 时,进入了拥塞避免算法。

拥塞避免

一般来说,慢启动阀值 ssthresh 是 65535 字节,cwnd到达慢启动阀值

  • 每收到一个 ACK 时,cwnd = cwnd + 1/cwnd
  • 当每过一个 RTT 时,cwnd = cwnd + 1

显然这是一个线性上升的算法,避免过快导致网络拥塞问题。

由指数级增长变为线性增长。

假设 ssthresh = 8 ,没收到一个ACK,增加1/8,收到8个ACK之后,cwnd+1。

image.png

拥塞发生

如果网络拥塞发生丢包时,会有两种情况:

  • RTO超时重传
  • 快速重传

RTO超时重传

如果发生RTO超时重传,会使用拥塞发生算法。

  • 慢启动阈值 sshthresh = cwnd
  • 重置cwnd=1
  • 开始新一轮的慢启动过程

image.png

快速重传

发送方如果因为网络问题接收到3个连续重复的ACK时,就会触发快速重传。不必等待超时。

发⽣快速重传的拥塞发⽣算法:

  • 拥塞窗口大小 cwnd = cwnd / 2
  • 慢启动阀值 ssthresh = cwnd
  • 进入快速恢复算法

调整拥塞窗口为一半,然后进入快速恢复算法。

快速恢复

快速重传一般和快速恢复同时使用。

快速恢复算法认为,还有三个重复ACK收到,说明网络也没有那么糟糕。不需要像RTO超时重传一样直接进入慢启动阶段。

  • cwnd = sshthresh + 3(窗口扩大3个)
  • 重传重复的那几个 ACK(即丢失的那几个数据包)
  • 如果再收到重复的 ACK,那么 cwnd = cwnd +1
  • 如果收到新数据的 ACK 后, cwnd = sshthresh。因为收到新数据的 ACK,表明恢复过程已经结束,可以再次进入了拥塞避免的算法了。

image.png

避免直接进入慢启动阶段。