TCP面试系列慢开始与拥塞避免

TCP 面试系列第七弹,TCP 慢开始与拥塞避免。

在讨论几种拥塞控制方法前,为了方便大家理解,我们假定:

  • 数据是单方向传送,而另一个方向只传送确认
  • 接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定

拥塞窗口 cwnd (congestion window) 由发送方维持的一个状态变量。发送方让自己的发送窗口等于拥塞窗口。

拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。

发送方控制拥塞窗口的原则:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。

下面从慢开始算法讨论,cwnd 是如何变化的。

慢开始算法的核心思想:在事先不清楚网络的负荷情况下,由小到大逐渐增大拥塞窗口数值。

在刚开始发送报文段时,先把拥塞窗口 cwnd 设置为最大报文段 MSS 的数值。

之后在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个 MSS 的数值。

用这样的方法逐步增大发送方的拥塞窗口 cwnd,从而使分组注入到网络的速率更加合理。

如下图所示,每经过一个传输轮次(transmission round),拥塞窗口 cwnd 就加倍。

经过上面的分析后,现在我们思考下,为什么叫慢开始,这个慢是指什么慢?

慢开始的“慢”并不是指 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时先设置 cwnd = 1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大 cwnd。这当然比按照大的 cwnd 一下子把许多报文段突然注入到网络中要“慢得多”。

慢开始有没有什么缺陷?或者没有考虑完善的情况?

我们已经知道,拥塞窗口 cwnd 每经过一个传输轮次,拥塞窗口 cwnd 就加倍。如果拥塞窗口增长速率过大,也会带了网络拥塞。为了解决这个问题,还需要设置一个慢开始门限 ssthresh。

慢开始门限 ssthresh 规则如下:

  • 当cwnd < ssthresh时,使用上述的慢开始算法。
  • 当cwnd > ssthresh时,停止使用慢开始算法而改用拥塞避免算法。
  • 当cwnd = ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。
  • 那么什么是拥塞避免算法?拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加1,而不是加倍。这样,拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

具体例子,如下图所示:慢开始和拥塞避免算法的实现举例

记住,无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。

根据上面的解释,分析一下图示曲线的走势情况:

  1. TCP 连接进行初始化时,把拥塞窗口 cwnd 置为 1。
  2. 当执行慢开始算法时,拥塞窗口 cwnd 的初始值为 1。以后发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值加 1,然后开始下一轮的传输(请注意,图示的横坐标是传输轮次)。因此拥塞窗口 cwnd 随着传输轮次按指数规律增长。当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时(即当 cwnd = 16时),就改为执行拥塞避免算法,拥塞窗口按线性规律增长。
  3. 假定拥塞窗口的数值增长到 24 时,网络出现超时(这很可能就是网络发生拥塞了)。更新后的 ssthresh 值变为 12(即变为出现超时时的拥塞窗口数值 24的一半),拥塞窗口再重新设置为 1,并执行慢开始算法。当 cwnd = ssthresh = 12 时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时间增加一个 MSS 的大小。

对于上面的描述,我们可以概括为乘法减小加法增大

“乘法减小”:指不论在慢开始阶段还是拥塞避免阶段,只要出现超时(即很可能出现了网络拥塞),就把慢开始门限值 ssthresh 减半,即设置为当前的拥塞窗口的一半(与此同时,执行慢开始算法)

”加法增大”:是指执行拥塞避免算法后,使拥塞窗口缓慢增大。

上面两种算法合起来就是我们常称的 AIMD 算法。