TCP协议的秘密武器:流量控制与拥塞控制

发布时间 2023-09-24 16:58:56作者: 努力的小雨

TCP可靠性传输

相信大家都熟知TCP协议作为一种可靠传输协议,但它是如何确保传输的可靠性呢?

要实现可靠性传输,需要考虑许多因素,比如数据的损坏、丢失、重复以及分片顺序混乱等问题。如果不能解决这些问题,就无法实现可靠传输。

因此,TCP采用了序列号、确认应答、重发控制、连接管理和窗口控制等机制来实现可靠性传输。

在本文中,我们将重点介绍TCP的滑动窗口、流量控制和拥塞控制。重传机制将在下一章节单独讲解。

流量控制

流量控制实际上是生产者和消费者之间微妙关系的一个具体体现。你可能在工作中或者面试中经常遇到这种考察场景。如果生产者的生产能力大大超过消费者的消费能力,就会导致队列无限增长。更严重的情况是,你可能知道当RabbitMQ消息堆积过多时,会导致整个MQ服务器性能下降。TCP也是类似的道理,如果不加以控制,过多的消息都被放到网络中,消费者已经超过了其能力范围,而生产者仍在重复发送,这会大大影响网络传输性能。

为了解决这种现象,TCP提供了一种机制,让发送方根据接收方的实际接收能力来控制发送的数据量,这就是所谓的流量控制。接收方维护一个接收窗口,而发送方维护一个发送窗口。需要注意的是,这些窗口只针对单个TCP连接,而不是所有连接共享一个窗口。

TCP通过使用一个接收窗口的变量来提供流量控制。接收窗口给发送方一个指示,告诉它还有多少可用的缓存空间。发送端根据接收端的实际接受能力来控制发送的数据量。

接收端主机会通知发送端主机自己可以接收数据的大小,发送端会发送不超过这个限度的数据。这个大小限度就是窗口大小,你还记得TCP首部吗?有一个接收窗口字段,它用于指示接收方能够或愿意接收的字节数量。

发送端主机会定期发送一个窗口探测包,用于探测接收端主机是否还能够接受数据。当接收端的缓冲区面临数据溢出的风险时,窗口大小的值也会相应地被设置为一个更小的值,通知发送端控制数据发送量。

以下是一个流量控制示意图:

image

为了确保接收端主机能够及时处理数据,发送端主机会根据接收端主机的窗口大小进行流量控制。这样可以防止发送端主机一次发送过大的数据导致接收端主机无法处理。

如上图所示,假设主机B的缓冲区已经满了,在接收到报文段2000-2999之后,它不得不暂停接收数据。为了解决这个问题,主机A会发送窗口探测包,这个包非常小,只有一个字节。主机B会根据接收缓冲区的情况更新接收窗口大小,并发送窗口更新通知给主机A。然后主机A可以继续发送报文段。

在上述发送过程中,窗口更新通知有可能会丢失。一旦丢失,发送端就不会继续发送数据。为了避免这种情况发生,窗口探测包会被随机发送,以确保通知的可靠传输。

拥塞控制

在介绍拥塞控制之前,我们需要了解除了接收窗口和发送窗口之外,还有一个拥塞窗口,它主要用于解决发送方以什么速度开始发送数据给接收窗口的问题。因此,拥塞窗口也是由TCP发送方进行维护的。我们需要一个算法来决定发送多少数据是合适的,因为发送数据过少或过多都不理想,所以就有了拥塞窗口的概念。

在之前的流量控制中,我们避免的是发送方的数据填满接收方的缓存,但是我们并不知道网络中发生了什么情况。通常情况下,计算机网络处于一个共享的环境中。因此,可能会因为其他主机之间的通信而导致网络拥堵。

当网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包的延迟和丢失等问题。此时,TCP会重传数据,但是重传会增加网络的负担,导致更大的延迟和更多的丢包。这种情况会进入恶性循环,并不断放大。

因此,TCP不能忽略网络上发生的情况。当网络发生拥塞时,TCP会自我牺牲,降低发送的数据量。

因此,拥塞控制就应运而生,其目的是避免发送方的数据填满整个网络。为了调节发送方应该发送的数据量,所以TCP定义了一个叫做拥塞窗口的概念。拥塞控制的算法会根据网络的拥塞程度来调整拥塞窗口的大小,从而控制发送方的数据量。

什么是拥塞窗口?和发送窗口有什么关系呢?

拥塞窗口(Congestion Window)是发送方维护的一个状态变量,它决定了发送方可以发送的数据量。拥塞窗口会根据网络的拥塞程度动态变化。

发送窗口(Sending Window)是发送方和接收方之间约定的一个窗口大小,表示接收方可以接收的数据量。拥塞窗口和发送窗口有关系,发送窗口的值通常等于拥塞窗口和接收窗口中的最小值,即swnd = min(cwnd, rwnd)。

拥塞窗口cwnd的变化规则如下:

如果网络中没有出现拥塞,即没有发生超时重传,拥塞窗口会增大;

如果网络中出现了拥塞,拥塞窗口会减小。

发送方通过观察是否在规定时间内收到ACK确认报文来判断网络是否出现了拥塞。如果发送方在规定时间内没有收到ACK确认报文,就认为网络出现了拥塞。

除了拥塞窗口,下⾯我们就该聊⼀下 TCP 的拥塞控制算法(TCP congestion control algorithm) 了。TCP 拥塞控制算法主要包含三个部分:

  • 慢启动(Slow Start):初始时,拥塞窗口cwnd的值比较小,发送方以指数增加的方式增大拥塞窗口,以快速适应网络的容量。
  • 拥塞避免(Congestion Avoidance):拥塞窗口超过一定阈值后,发送方以线性增加的方式增大拥塞窗口,以减缓拥塞窗口的增长速度,避免过载网络。
  • 快速恢复(Fast Recovery):如果发生拥塞,发送方将拥塞窗口减半,并进入快速恢复状态,通过接收到的重复ACK来确定网络恢复的位置,然后继续增加拥塞窗口。

慢启动

当一条TCP连接建立时,拥塞窗口cwnd的初始值会设为一个MSS(最大报文段长度)的较小值。这样,初始的发送速率大约是MSS/RTT(往返时间)字节/秒。实际可用带宽通常比MSS/RTT大得多,因此TCP希望找到最佳的发送速率,可以通过慢启动的方式来实现。

在慢启动过程中,拥塞窗口cwnd的值会初始化为1个MSS,并且每次传输的报文段确认后,cwnd的值会增加一个MSS,即cwnd的值会变为2个MSS。之后,每成功传输一个报文段,cwnd的值就会翻倍,依此类推。具体的增长过程如下图所示。

image

然而,发送速率不可能一直增长,增长总有结束的时候。那么,何时结束发送速率的增长呢?慢启动通常会使用以下几种方式来结束发送速率的增长。

第一种方式是在慢启动的发送过程中出现丢包的情况。当发生丢包时,TCP会将发送方的拥塞窗口cwnd设置为1,并重新开始慢启动的过程。此时,引入了一个慢启动阈值ssthresh的概念,它的初始值就是产生丢包的cwnd的值的一半。也就是说,当检测到拥塞时,ssthresh的值就是窗口值的一半。

第二种方式是直接和慢启动阈值ssthresh的值相关联。因为当检测到拥塞时,ssthresh的值就是窗口值的一半,那么当cwnd大于ssthresh时,每次翻番都可能会出现丢包。所以,最好的方式就是将cwnd的值设为ssthresh,这样TCP就会转为拥塞控制模式,结束慢启动。

慢启动结束的最后⼀种⽅式就是如果检测到 3 个冗余 ACK,TCP 就会执⾏⼀种快速重传并进⼊恢复状态。(如果不清楚为什么会有三次同样的ACK报文,在重传机制中会单独讲解)

拥塞避免

当TCP进入拥塞控制状态后,cwnd的值会被设为拥塞阈值ssthresh的一半。这意味着无法每次收到报文段后都将cwnd的值翻倍。相反,采用了一种相对保守的方式,即每次传输完成后,只将cwnd的值增加一个MSS(最大报文段长度)。例如,即使收到了10个报文段的确认,但cwnd的值只会增加一个MSS。这是一种线性增长模式,它也有一个增长上限。当出现丢包时,cwnd的值将变为一个MSS,ssthresh的值将设为cwnd的一半;或者当收到3个冗余的ACK响应时,也会停止MSS的增长。如果在将cwnd的值减半后仍然收到3个冗余ACK,那么ssthresh的值将记录为cwnd值的一半,并进入快速恢复状态。

快速恢复

在快速恢复(Fast Recovery)状态中,对于每个收到的冗余ACK(即不是按顺序到达的ACK),拥塞窗口cwnd的值会增加一个MSS。这是为了利用网络中已经传输成功的报文段,尽可能地提高传输效率。

当丢失的报文段的一个ACK到达时,TCP会降低cwnd的值,然后进入拥塞避免状态。这是为了控制拥塞窗口的大小,避免继续加重网络拥塞。

如果在拥塞控制状态后出现超时,说明网络状况变得更加严重,TCP会从拥塞避免状态迁移到慢启动状态。此时,拥塞窗口cwnd的值被设置为1个MSS(最大报文段长度),而慢启动阈值ssthresh的值被设置为cwnd的一半。这样做的目的是为了在网络恢复后,重新逐渐增加拥塞窗口的大小,以平衡传输速率和网络拥塞程度。

总结

TCP协议作为一种可靠传输协议,通过序列号、确认应答、重发控制、连接管理和窗口控制等机制来实现可靠性传输。其中,流量控制机制通过发送方根据接收方的实际接收能力来控制发送的数据量,避免了网络拥堵和性能下降的问题。而拥塞控制机制则通过调节发送方的数据发送量,避免了网络拥塞的发生。拥塞窗口和发送窗口的概念相互关联,通过动态调整拥塞窗口的大小来控制发送方的数据量。慢启动、拥塞避免和快速恢复是TCP拥塞控制算法的三个主要部分,通过不同的策略来调节拥塞窗口的大小,以适应网络的容量和拥塞程度。

在下一章节中,我们将详细讲解TCP的重传机制。重传机制是TCP实现可靠传输的重要组成部分,通过对丢失、损坏或延迟的数据进行重传,确保数据的可靠性传输。重传机制的实现原理和策略将在下一章节中进行详细的介绍和解析。敬请期待!