TCP模块除了要进行流量控制外,还有一个重要的任务,就是提高网络利用率,降低丢包率,并保证网络资源对每条TCP连接的数据流的公平性。这就是拥塞控制要解决的问题。
在计算机网络中的链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源。在某段时间,如果对网络中的某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要发生变化,这种情况就叫做拥塞(congestion)。可以把出现网络拥塞的条件写成如下的关系式:
Demand for resources:表示对资源的需求
Available resources:表示可用资源
若网络中有许多资源同时呈现供应不足,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。
造成网络拥塞的可能原因分析:
1、网络结点存储空间有限。当某个网络结点的缓存容量太小时,到达该结点的IP分组因无存储空间暂存而不得不被丢弃。
2、网络结点处理机性能有限。路由器中的CPU主要执行缓存区排队、更新路由表、进行路由选择等功能。如果其工作效率不能满足高速链路的需求,就会造成网络拥塞。
3、传输链路的带宽有限。当数据的发送速率很高,而网络链路带宽较低时,就会导致整个网络的输入负载超过网络的承受能力,也会造成网络拥塞。
4、网络中某部分刚发生故障。
拥塞控制的概念:所谓拥塞控制就是防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不至于过载。
拥塞控制的前提条件:网络能够承受现有的网络负荷。
拥塞控制与流量控制的区别:
拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。
流量控制往往是指点对点通信量的控制,是个端到端的问题(接收端控制发送端)。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
流量控制和阻塞控制实例:
可以用一个例子来说明这种区别。设某个光纤网络的链路传输速率为 1000 Gbit/s。有一台巨型计算机向一台个人电脑以 1 Gbit/s的速率传送文件。显然,网络本身的带宽是足够大的,因而不存在产生拥塞的问题。但流量控制却是必须的,因为巨型计算机必须经常停下来,以便使个人电脑来得及接收。(流量控制)
但如果有另一个网络,其链路传输速率为 1 Mbit/s,而有1000台大型计算机连接在这个网络上,假定其中的 500 台计算机分别向其余的 500 台计算机以100 kbit/s的速率发送文件。那么现在的问题已不是接收端的大型计算机是否来得及接收,而是整个网络的输入负载是否超过网络所能承受的。(拥塞控制)
拥塞控制和流量控制之所以常常被弄混,是因为某些拥塞控制算法是向发送端发送控制报文,并告诉发送端,网络已经出现麻烦,必须放慢发送速率。这点又和流量控制是很相似的。
拥塞控制代价:首先需要获得网络内部流量分布的信息。在实施拥塞控制之前,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外的开销。拥塞控制还需要将一些资源(如缓存、带宽等)分配给个别用户(或一些类别的用户)单独使用,这样就使得网络资源不能更好地实现共享。十分明显,在设计拥塞控制策略时,必须全面衡量得失。如下图所示:
在图 5-23 中的横坐标是提供的负载(offered load),代表单位时间内输入给网络的分组数目。因此提供的负载也称为输入负载或网络负载。
纵坐标是吞吐量(throughput),代表单位时间内从网络输出的分组数目。具有理想拥塞控制的网络,是在吞吐量饱和之前,网络吞吐量应等于网络的负载,故吞吐量曲线是 斜率 k=1 的斜线。但当提供的负载超过某一限度时,由于网络资源受限,吞吐量不再增长而保持为水平线,即吞吐量达到饱和。这就表明提供的负载中有一部分损失掉了(例如,输入到网络的某些分组被某个网络结点丢弃了)。虽然如此,在这种理想的拥塞控制作用下,网络的吞吐量仍然维持在其所能达到的最大值。
但是,实际网络的情况就很不相同了。从 图 5-23 可看出,随着提供的负载的增大,网络吞吐量的增长速率逐渐减小。也就是说,在网络吞吐量还未达到饱和时,就已经有一部分的输入分组被丢弃了。当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态。更值得注意的是,当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了网络拥塞状态。当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作,这就是所谓的死锁(deadlock)。
从原理上讲,寻找拥塞控制的方案无非就是寻找使上面的拥塞控制表达式不成立的条件。如增大网络的某些可用资源(如业务繁忙时增加一些链路,增大链路的带宽,或使额外的通信量从另外的通路分流),或减少一些用户对某些资源的需求(如拒绝接受新的建立连接的请求,或要求用户减轻其负荷,这属于降低服务质量)。但是,在采取某种措施时,还必须考虑到该措施所带来的其他影响。
TCP 进行拥塞控制的过程有四个部分,分别是:慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit) 和 快恢复(fast recovery)(见 2009 年 9 月 公布的草案标准 RFC 5681)。
拥塞控制算法在 Linux 下有多种实现,比如:reno算法、vegas算法 和 cubic算法等。它们或者部分或者全部实现了上述四个部分。
/proc/sys/net/ipv4/tcp_congestion_control 内核变量指示了主机当前所使用的拥塞控制算法。
下面介绍这些算法的原理。为了集中精力讨论拥塞控制,我们假定:
(1)数据是单方向传送,对方只传送确认报文段。
(2)接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定。
(3)以TCP报文段的个数为讨论问题的单位,而不是以字节为单位。
TCP 的拥塞控制也叫做基于窗口的拥塞控制。为此,发送方维持了叫做拥塞窗口 cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地变化。发送方让自己的发送窗口等于拥塞窗口。
发送方控制拥塞窗口cwnd的原则是:只有网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提供网络的利用率。但只要网络出现拥塞或者用可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。
发送方又是如何知道网络出现了拥塞呢?我们知道,当网络发生拥塞时,路由器就要丢弃分组。因此,只要发送方没有按时收到应当到达的确认报文,也就是说,只要出现了超时,就可以猜测网络可能出现了拥塞。现在通信线路的质量一般都很好,因传输出差错而丢弃分组的概率很小(远小于1%)。因此,判断网络拥塞的依据就是出现了超时。
下面我们将讨论拥塞窗口 cwnd 的大小是怎样变化的。我们从“慢开始算法”讲起。
慢开始算法的思路是这样的:当主机开始发送数据时,由于不清楚网络的负荷情况,所以如果立即把大量数据字节注入到网络,那么就有可能引起网络拥塞。经验证明,较好的方法是先探测一下,即由小大到逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。
旧的规定是这样的:在刚刚开始发送报文段时,先把初始拥塞窗口 cwnd 设置为 1 至 2 个发送方的最大报文段 SMSS(Sender Maximum Segment Size) 的数值,但新的 RFC 5681 把初始拥塞窗口 cwnd 设置为不超过 2 至 4 个 SMSS 的数值。具体的规定如下:
- 若 SMSS > 2190 字节,
则设置初始拥塞窗口 cwnd = 2 × SMSS 字节,且不得超过 2 个报文段。
- 若 (SMSS > 1095 字节) && (SMSS ≤ 2190 字节),
则设置初始拥塞窗口 cwnd = 3 × SMSS 字节,且不得超过 3 个报文段。
- 若 SMSS ≤ 1095 字节,
则设置初始拥塞窗口 cwnd = 4 × SMSS 字节,且不得超过 4 个报文段。
由此可见,这个规定就是限制初始拥塞窗口 cwnd 的字节数。
慢开始规定,在每收到一个对新的报文段的确认后,可以把拥塞窗口增加最多一个 SMSS 的数值。即:
拥塞窗口 cwnd 每次的增加量 = min(N, SMSS)
其中,N 是原先未被确认的、但现在被刚收到的确认报文段所确认的字节数。不难看出,当 N < SMSS 时,拥塞窗口每次的增加量要小于 SMSS。用这样的方法逐步增大发送方的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。
慢开始
下面用例子说明慢开始算法的原理。请注意,虽然实际上 TCP 是用字节数作为窗口大小的单位。但为了叙述方便起见,我们用报文段的个数作为窗口大小的单位,这样可以使用较小的数字来阐明拥塞控制的原理。
1. 在一开始发送发先设置 cwnd = 1,发送第一个报文段 M1,接收方收到后确认 M1。
2. 发送方收到对M1的确认后,把cwnd从1增大到2,于是发送方接着发送M2和M3两个报文段,接收方收到后发回对M2和M3的确认。
3. 发送方每收到一个对新报文段的确认(重传的不算在内)就使发送方的拥塞窗口加1,因此发送方在收到两个确认后,cwnd 就从2增大到4,并可发送M4~M7共4个报文段。
4. 因此使用慢开始算法后,每经过一个传输轮次(transmission round),拥塞窗口 cwnd 就加倍。
什么是RTT
这里我们使用了一个名词——传输轮次。
从 图-24 可以看出,一个传输轮次所经历的时间其实就是往返时间RTT(请注意,RTT 并非是恒定的数值)。
使用“传输轮次”是更加强调:把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。
例如,拥塞窗口 cwnd = 4 个报文段,那么这时的往返时间 RTT 就是发送方连续发送 4 个报文段,并收到这 4 个报文段的确认,总共经历的时间。
我们还要指出,慢开始的“慢”并不是指 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时先设置 cwnd = 1,使得发送方在开始时只发送一个报文段(目的是试探一下网络当前的拥塞情况),然后再逐渐增大 cwnd。这当然比设置大的 cwnd 值一下子把许多报文段注入到网络中要“慢得多”。这对防止网络出现拥塞是一个非常好的方法。
顺便指出,图 5-24 只是为了说明慢开始算法的原理。在 TCP 的实际运行中,发送方只要收到一个对新报文段的确认,其拥塞窗口 cwnd 就立即加 1,并开始立即发送新的报文段,而不需要等这个传输轮次中所有的确认都收到后(如图 5-24 所示的那样)再发送新的报文段。
什么是 ssthresh 状态变量,以及作用
为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。慢开始门限 ssthresh 的用法如下:
- 当 cwnd < ssthresh 时,使用上述的慢开始算法。
- 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
- 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。
拥塞避免
拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增长,即每经过一个往返时间 RTT 就把发送方的 拥塞窗口 cwnd 加 1,而不是像慢开始阶段那样加倍增长。因此在拥塞避免阶段就有“加法增大” AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
上面说的 “每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1”,实际上应当是“拥塞窗口仅增加一个 MSS 的大小,单位是字节”。在具体实现拥塞避免算法的方法时可以这样来完成:只要收到一个新的确认,就是拥塞窗口 cwnd 增加 (MSS × MSS / cwnd)个字节。
例如,假定 cwnd = 10 个 MSS的长度,而 MSS = 1460 字节。发送方可一次一连发送 14600 字节(即10个报文段)。
假定接收方每收到一个报文段就发回一个确认。于是发送方每收到一个新的确认,就把拥塞窗口稍微增大一些,即增大 0.1 MSS = 146 字节。经过一个往返时间 RTT (或一个传输轮次)后,发送方共收到 10 个新的确认,拥塞窗口 cwnd 就增大了 1460字节,正好是一个 MSS 的大小。
下图的图 5-25 用具体的例子说明了在拥塞控制的过程中,TCP 的拥塞窗口 cwnd 是怎样变化的?图中的数字 ① 至 ⑤ 是特别要注意的几个点。现假定 TCP 的发送窗口等于拥塞窗口。
当 TCP 建立连接进行初始化时,把拥塞窗口 cwnd 置为1。为了便于理解,图中的窗口单位不使用字节而使用报文段的个数。在本例中,慢开始门限的初始值设置为 16 个报文段,即 ssthresh = 16。在执行慢开始算法时,发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值加 1,然后开始下一轮的传输(请注意,图 5-25 的横坐标是传输轮次,不是时间)。因此拥塞窗口 cwnd 随着传输轮次按指数规律增长。当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时(图中的点①,此时拥塞窗口 cwnd = 16),就开始改为执行拥塞避免算法,拥塞窗口按线性规律增长。但请注意,“拥塞避免”并非完全能够避免了拥塞。“拥塞避免” 是说把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。
当拥塞窗口 cwnd = 24 时,网络出现了超时(图中的点②),发送方判断为网络拥塞。于是调整门限值 ssthresh = cwnd / 2 = 12,同时设置拥塞窗口 cwnd =1,又重新进入慢开始阶段。
按照慢开始算法,发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值加1。当拥塞窗口 cwnd = ssthresh = 12 时(图中的点③,这是新的 ssthresh 值),改为执行拥塞避免算法,拥塞窗口按线性规律增大。
当拥塞窗口 cwnd = 16 时(图中的点④),出现了一个新的情况,就是发送方一连收到 3 个对同一报文段的重复确认(图中记为 3-ACK)。关于这个问题,解释如下:
有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口 cwnd 又置为1,因而降低了传输效率。
为了解决这个问题,需要用到快重传算法。采用快重传算法的目的是:可以让发送方尽早知道发生了个别报文段的丢失。
快重传
所谓 “快重传” ,就是让发送方尽快重传丢失报文段,而不是等待超时重传计时器超时后再重传。有以下几点要求:
- 要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认。
- 即使接收方收到了失序的报文段,也要立即发出对已收到的报文段的重复确认。
- 发送方只要一连收到 3 个对同一报文段的重复确认,就将相应的报文段立即重传,而不是等待报文段的超时重传计时器超时后再重传。
如图 5-26 所示:
1、接收方收到了报文段 M1 和 M2 后,都分别及时发出了确认。
2、现假定接收方没有收到 M3 但却收到了 M4。本来接收方可以什么都不做。但按照快重传算法,接收方必须立即发送对 M2 的重复确认,以便让发送方及早知道接收方没有收到报文段 M3。发送方接着发送 M5 和 M6。
3、接收方收到 M5 和 M6 后,仍要再次分别发出对 M2 的重复确认。这样,发送方共收到了接收方的 4 个对 M2 的确认,其中后 3 个都是重复确认。
4、快重传算法规定,发送方只要一连收到 3 个对同一报文段(该例子中指的是报文段 M2)的重复确认,就知道接收方确实没有收到报文段 M3,因而应当立即进行重传(即 “快重传”),这样发送方就不会出现超时,也就不会认为出现了网络拥塞。
使用快重传算法可以使整个网络的吞吐量提供约 20%。
快恢复
在上图的图 5-25 中的点④,发送方知道现在只是丢失了个别的报文段,而不是发生了网络拥塞。于是不启动慢开始,而是执行快恢复算法。
这时,发送方调整门限值 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8(见图 5-25 中的点⑤),并开始执行拥塞避免算法。
<注> 虽然 RFC 5681 给出了根据已发送出去但还未收到确认的数据的字节数来设置 ssthresh 值的新的计算公式,即:
ssthresh = max { FlightSize / 2, 2 × SMSS}
这里 FlightSize 表示的是正在网络中传送的数据量。但在讨论拥塞控制原理时,我们为了简化问题,采用的是把慢开始门限值减半的方式来表述。
请注意,也有的快恢复实现是把快恢复开始时的拥塞窗口 cwnd 的值再增大一些(增大 3 个报文段的长度),即:新的 ssthresh + 3 × MSS。这样做的理由是:既然发送发收到了3个重复的确认,就表明有3个分组已经离开了网络。这 3 个分组不再消耗网络的资源而是停留在接收方的接收缓存中(接收方发送出 3 个重复的确认就证明了这个事实)。可见网络中并不是堆积了分组而是减少了 3 个分组。因此可以适当把拥塞窗口扩大些。
从图 5-25 可以看出,在拥塞避免阶段,拥塞窗口 cwnd 是按照线性规律增大的,这常称为“加法增大” AI(Additive Increase)。而一旦出现超时或 3 个重复的确认,就要把慢开始门限值 ssthresh 设置为当前 拥塞窗口值 cwnd 的一半,并大大减小拥塞窗口的数值。这常称为 “乘法减小” MD(Multiplicative Decrease)。二者合在一起就是所谓的 AIMD 算法。
快恢复的两个特点
(1)当发送方连续收到 3 个重复确认时,就执行“乘法减小”算法,把慢开始门限值减半,这是为了预防网络发生拥塞。
(2)由于发送方认为当前网络没有发生拥塞,因此现在不执行慢开始算法,而是把拥塞窗口值 cwnd 设置为慢开始门限减半后的值,然后开始执行拥塞避免算法,使拥塞窗口值线性增大。
快恢复和慢开始的区别
慢开始算法只是在TCP连接建立时或是遇到网络拥塞后才使用,快恢复是在遇到 3-ACK 时触发,常常伴随着快重传算法。
对图 5-25 示例中的整个流程进行一下梳理:
假定条件一:发送窗口 = 拥塞窗口
假定条件二:接收方总是有足够大的缓存空间
<备注> 为了便于理解,上图中的窗口单位不使用字节而是使用报文段的个数。
1、当TCP连接进行初始化时,把拥塞窗口值 cwnd 置为 1;同时把慢开始门限的初始值设置为 16 个报文段的长度,即:ssthresh = 16。
2、开始执行慢开始算法。发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1。一个传输轮次,拥塞窗口值就增加一倍。即拥塞窗口值随着传输轮次按指数规律增长。
3、当拥塞窗口增大到等于慢开始门限值时(图 5-25 中的点①),此时拥塞窗口 cwnd = 16。
4、改为执行拥塞避免算法。每经过一个传输轮次,拥塞窗口 cwnd 就增加 1。此时,拥塞窗口随着传输轮次按线性规律增长。
5、当拥塞窗口 cwnd = 24 时,发生了超时现象(图 5-25 中的点②),发送方判断为网络拥塞,于是调整门限值 ssthresh=cwnd/2=12,同时重新设置拥塞窗口 cwnd =1,进入慢开始阶段。
6、按照慢开始算法,发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1。一个传输轮次,拥塞窗口值就增加一倍。
7、当拥塞窗口 cwnd 增长到等于 慢开始门限值 ssthresh = 12 时(图 5-25 中的点③,这是新的 ssthresh 值),改为执行拥塞避免算法,拥塞窗口按线性规律增长。
8、当拥塞窗口 cwnd = 16 时(图 5-25 中的点④),出现了一个新的情况,就是发送方一连收到 3 个对同一报文段的重复确认(图 5-25 中记为 3-ACK)。
9、根据图 5-25 中的点④的情况,发送方知道了现在只是个别报文段出现了丢失的情况,于是不启动慢开始算法,而是执行快恢复算法。
10、这时,发送方调整门限值 ssthresh = cwnd/2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8(图 5-25 中的点⑤),并开始执行拥塞避免算法。
总结
通过上面的阐述,我们可以了解到TCP的拥塞控制的4个算法是相辅相成、环环相扣的。我们需要知道在什么情况下应该使用哪种算法。
从 图 5-27 流程图中,我们可以很清晰地看到在拥塞控制的各个阶段,采用哪种算法。例如,在慢开始阶段,如果出现了超时(即出现了网络拥塞)或出现了 3-ACK,发送方应采取什么措施。
上文中所阐述的拥塞控制,我们设置了一个假定条件:接收方总是有足够大的缓存空间。因而发送窗口的大小由网络的拥塞程度决定。但实际上接收方的缓存空间总是有限的。接收方根据自己的接收能力设定了一个接收方窗口 rwnd,并把这个窗口值写入 TCP 报文段的首部中的窗口字段,传送给发送方。因此,接收方窗口 又称为 通知窗口(advertised window)。因此,从接收方对发送方的流量控制的角度考虑,发送方的发送窗口一定不能超过对方给出的接收方窗口值 rwnd。
如果把本节所讨论的拥塞控制和接收方对发送发的流量控制一起考虑的话,那么很显然,发送方的窗口的上限值应当取为接收方窗口 rwnd 和 拥塞窗口 cwnd 这两个变量中较小的一个,也就是说:
发送方窗口值的上限值 = Min[rwnd, cwnd]
- 当 rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。
- 反之,当 cwnd < rwnd 时,则是网络的拥塞程度限制发送方窗口的最大值。
也就是说,rwnd 和 cwnd 中数值较小的一个,控制了发送方发送数据的速率。
上一节讨论的 TCP 拥塞控制并没有和网络层采取的策略联系起来。其实,它们之间有着密切的关系。
在网络层,也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
例如,假定一个路由器对某些分组的处理时间特别长,那么这就可能使这些IP分组中的数据部分(即TCP报文段)经过很长时间才能达到终点,结果引起发送方对这些报文段的超时重传。根据前面所讲的,重传会使 TCP 连接的发送端认为在网络中发生了拥塞。于是在 TCP 的发送端就采取了拥塞控制措施,但实际上网络并没有发生数拥塞。
网络层的策略对 TCP 拥塞控制影响最大的就是路由器的分组丢弃策略。在最简答的情况下,路由器的队列通常都是按照 “先进先出” FIFO(First In First Out) 的规则处理到来的IP分组。由于队列长度总是有限的,因此当队列满时,以后再到达的所有分组(如果能继续排队,这些分组都将排在队列的尾部)都将被丢弃。这就叫做尾部丢弃策略(tail-drop policy)。
路由器的尾部丢弃策略往往会导致一连串分组的丢失,这就使发送方出现超时重传,使 TCP 进入拥塞控制的慢开始状态,结果就是使 TCP 连接的发送方突然把数据的发送速率降低到很小的数值。更为严重的是,在网络中通常有很多的 TCP 连接(它们有不同的源点和终点),这些连接中的报文段通常是复用在网络层的 IP 数据报中传送。在这种情况下,若发生了路由器中的尾部丢弃,就有可能会同时影响到很多条TCP 连接,结果使这很多条 TCP 连接在同一时间突然都进入到慢开始状态。这在 TCP 的术语中称为全局同步(global syncronization)。全局同步使得全网的通信量突然下降了很多,而在网络恢复正常后,其通信量又突然增大很多。
为了避免发生网络中的全局同步现象,在 1998 年提出了 主动队列管理 AQM(Active Queue Management)。所谓 “主动” 就是不要等到路由器的队列长度已经达到最大值时才不得不丢弃后面达到的分组。这样就太被动了。应当在队列长度达到某个值得警惕的数值时(即当网络拥塞有了某些拥塞征兆时),就主动丢弃达到的分组。这样就提醒了发送方放慢发送数据的速率,因而有可能使网络拥塞的程度减轻,甚至不出现网络拥塞。AQM 可以有不同的实现方法,其中曾流行多年的就是随机早期检测 RED(Random Early Detection)。RED 还有几个不同的名称,如 Random Early Drop 或 Random Early Discard(随机早期丢弃)。
实现 RED 时需要使路由器维持两个参数,即队列长度最小门限和最大门限。当每一个分组到达时,RED 就按照规定的算法先计算当前的平均队列长度。
(1)若平均队列长度小于最小门限,则把新到达的分组放入队列尾部进行排队。
(2)若平均队列长度超过最大门限,则把新到达的分组丢弃。
(3)若平均队列长度在最小门限和最大门限之间,则按照某一丢弃概率 p 把新到达的分组丢弃(这就体现了丢弃分组的随机性)。
由此可见,RED 不是等到已经发生网络拥塞后才把所有在队列尾部的分组全部丢弃,而是在检测到网络拥塞的早期征兆时(即路由器的平均队列长度达到一定数值时),就以概率 p 丢弃个别的分组,让拥塞控制只在个别的 TCP 连接上进行,因而避免发生全局性的拥塞控制。
在 RED 的操作中,最难处理的就是概率 p 的选择,因为 p 并不是一个常数。对每一个到达的分组,都必须计算丢弃概率 p 的数值。IETF(Internet Engineering Task Force, 互联网工程任务组) 曾经推荐在互联网中的路由器使用 RED机制 [RFC 2309],但多年的实践证明,RED 的使用效果并不理想。因此,2015年 公布的 [RFC 7567] 已经把过去的 [RFC 2309] 列为“陈旧的”,并且不再推荐使用 RED。对路由器进行主动队列管理 AQM 仍是必要的。AQM 实际上就是对路由器中的分组排队进行智能管理,而不是简单地把队列的尾部丢弃。现在已经有几种不同的算法来代替旧的 RED,但都还在实验阶段。目前还没有一种算法能够成为 IETF 的标准,读者可注意这方面的进展。
《计算机网络(第7版-谢希仁)》第5章
《Linux高性能服务器编程》第3章
TCP的拥塞控制(详解)
TCP的流量控制和阻塞控制
详解TCP中的拥塞控制