拥塞控制算法主要能够分为两个部分:在端系统使用的的源算法和在网络设备上使用的链路算法。其中端到端的网络拥塞控制算法一般根据接受到的ACK(Acknowledge character)确认包中包含的信息来调整拥塞控制窗口的大小,进而控制TCP链接的发送速率,譬如:TCP Tahoe, TCP Reno, TCP Vegas, TCP NewReno, TCP BIC, TCP CBIC, BBR等算法;在网络中间设备上(路由器、防火墙、交换机等)的拥塞控制算法(AQM-Active Queue Management-主动队列管理)一般根据设备中的缓存队列长度信息对网络拥塞控制程度进行判断,并将拥塞控制信息显示或隐式地告知端点,端点根据得到的拥塞控制信息对自身发送速率进行调整,譬如:FIFO, RED(Random Early Detection), ECN(Explicit Congestion Notification), FQ(Fair Queuing)等算法。html
本文主要针对端对端的TCP拥塞控制算法进行分析。算法
网络产生拥塞的根本缘由在于用户(或称端系统)提供给网络的负载大于网络资源容量和处理能力,即“需求”大于"供应".通常表现为数据时延增长、丢包几率增大、上层应用系统性能降低。以下图显示了拥塞发生的状况.缓存
拥塞老是发生在网络资源中资源“相对”短缺的位置。拥塞发生位置的不均衡反映了\(Internet\)不均衡性。网络
1.首先是资源分布的不均衡性。下图中的带宽分布是不均衡的,当以 \(1Mb/s\) 的速率从S向D发送数据时,在网关R会发生拥塞。dom
2.其次是流量分布的不均衡。下图中带宽分布是均衡的,当 \(A\) 和 \(B\) 都以 \(1Mb/s\) 的速率向 \(C\) 发送数据时,在网关 \(R\) 也会发生拥塞。tcp
拥塞产生的直接缘由有以下3点:函数
TCP 拥塞控制通常包含以下四个控制过程:慢启动 (slow start)、拥塞避免 (congestion avoidance)、快速重传 (fast retransmit) 和快速恢复 (fast recovery)。如图2.1 和图 2.2所示性能
早期的TCP在创建链接后,发送方将向网络发送多个数据包直到达到接受方声明的最大窗口为止。当两台主机在同一个局域网时,这种方式彻底可行。但若是发送方和接收方之间存在路由器和低速链路时,这种方式就会产生一些问题。一些路由器可能须要排列数据包于是产生缓存容量不足的问题,从而引发数据包丢失,致使传输超时,下降网络的吞吐量(throughput)。ui
用来避免上述状况的算法称为慢启动算法:当创建新的TCP链接时,拥塞窗口(\(cwnd-congestion \ window\))初始化为一个数据包大小。发送方首先发送1个数据包,而后等待反馈回来的该数据包的 \(ACK\) 确认包,当接受到相应的 \(ACK\) 后,拥塞窗口的值从1增长到2,此时,发送方一次就能发送2个数据包。当这个两个数据包的 \(ACK\) 都到达后,拥塞窗口的值就增长至4.依次类推,每通过一个 \(RTT\)(\(round-trip \ time\)) 的时间,cwnd的值将增长一倍。这种方式使得拥塞窗口的增加将随着 \(RTT\) 呈指数级 (\(exponential\)) 增加。发送方向网络中发送的数据量将急剧增长。spa
拥塞避免算法的做用是经过减缓 \(cwnd\) 值的增长速率推迟网络拥塞控制的发生,这样可以使得发送端在较长的一段时间内保持较高的数据传输率。该算法假设基于损坏而丢失包的比例很小(远小于\(1\%\))。所以,当出现丢包时,就代表源端和接收端之间的网络的某个地方可能出现了拥塞。包的丢失有两种暗示:定时器超时和接收到重复 \(ACK\)。
若是在慢启动阶段中,\(cwnd\) 增长至大于阈值 \(ssthresh\)(\(slow \ start \ threshold\)) 时,进入拥塞避免阶段,在每一个 \(RTT\) 时间内,若发送方收到对当前 \(cwnd\) 对应数据的应答后,\(cwnd\) 就比原来增长一个最大\(TCP\)数据包所对应的字节数,即 \(cwnd\) 线性增加。
不管是慢启动仍是拥塞避免,\(cwnd\) 老是增加,使得负载逐渐增大最终致使网络拥塞。一旦数据包丢失,发送方只能在定时器超时以后才启动数据重发过程,这样可能会致使网络资源的浪费或闲置。所以快速重传的机制是,当发生丢包事件时,接收方将返回多个对相同数据的重复应答,若发送方连续收到三个以上的 \(ACK\) ,则代表极可能一个数据包丢失了。此时,发送方没必要等重传定时器超时就从新传送那个可能丢失的数据包。TCP发送方利用快速重传算法检测和恢复数据包丢失。
在快速重传可能丢失的数据包以后,\(TCP\) 就执行拥塞避免算法(而不是慢启动算法),这就是快速恢复算法。此后,快速恢复算法将控制新数据的传送,直到收到一个非重复的 \(ACK\) 为止,之后开始执行拥塞避免而不是慢启动。快速恢复实际上是基于 "管道" 模型的 "报文守恒"(\(packet \ conservation\)) 的原则,控制的目标是使得网络中报文的个数保持恒定,只有当 "旧" 报文离开网络后,才能发送 "新" 报文进入网络。
快速恢复可以使大窗口下发生中度拥塞时依旧具备较高的吞吐量。对于没有执行慢启动的缘由是:收到重复的 \(ACK\) 意味着数据包已经丢失,而因为接收方收到另外一个数据包时才会产生重复的 \(ACK\) ,说明有另外的数据包已经到达了接收方的缓存区里。也就是说,在这两个端点之间还存在数据发送,\(TCP\) 不想进入慢启动而形成流量的忽然下降。
![]()
\(TCP-Tahoe\) 网络拥塞控制算法是最先的端到端的拥塞控制算法。算法仅做用于端点,嵌入在 TCP 协议中,由 \(Jacobson\) 在 1988 提出,此算法极大的提升了网络控制拥塞的能力,并成为 \(Internet\) 标准之一。
\(TCP-Tahoe\) 算法经过改变拥塞窗口的大小来调节 \(TCP\) 链接中端点的数据发送速率,使得端点具备了主动控制数据发送速率,避免网络拥塞的能力。该算法中对拥塞窗口的调节主要包括了慢启动、快速重传和拥塞避免三种策略。
流程:首先使用慢启动策略,将拥塞控制窗口大小设为1个 \(MSS\)(\(Maximum \ Segment \ size\), 最大报文长度),随后每收到一个ACK确认包,拥塞窗口大小便增长1个 \(MSS\)。当端点收到3个连续的 \(ACK\) 确认包或发生重传超时 (\(Retransmission \ Timeout, RTO\)) 时,则认为传输过程当中出现了数据包丢失,当即使用快速重传策略以从新传输丢失的数据包,重传结束后,将慢启动阈值 \(ssthresh\) 设置为当前拥塞窗口大小的一半,随后从新使用慢启动策略控制拥塞窗口大小,将其重置为1个 \(MSS\) 大小,当拥塞窗口大小增长至大于 \(ssthresh\) 时,则切换为拥塞避免策略。拥塞避免策略使拥塞窗口大小在 \(RTT\) 内增长一个 \(MSS\),直至再次检测到数据包丢失后从新使用慢启动策略, 算法依照数据包丢失状况来判断链路是否出现拥塞,并据此对拥塞窗口大小进行调节。
优势:\(TCP-Tahoe\) 算法在慢启动阶段发送速率较低时可以以指数形式快速增加,快速占用可用带宽,在发送速率接近链路带宽上限时在拥塞避免阶段以线性形式平稳增加,保持相对稳定。一方面能够探测出所处链路的最大带宽,另外一方面可以使得链路上存在的其余 \(TCP\) 链接出现拥塞,使其下降自身数据发送速率,让出一部分网络资源提供给新创建的 \(TCP\) 链接使用。同时 \(TCP-Tahoe\) 的快速重传策略也解决了 \(TCP\) 链接超时重传致使的大量网络带宽的浪费。
缺点: 在 \(TCP-Tahoe\) 中,一旦检测到数据包丢失,拥塞窗口大小即被重置为1个 \(MSS\) ,这样 \(TCP\) 链接在数据包传输恢复正常以前没法传输新的数据,形成了链路带宽的大量浪费。
![]()
基于 \(TCP-Tahoe\) 算法中的缺陷,Jacobson 提出了 \(TCP-Reno\) 拥塞控 制算法,在 $TCP -Tahoe $原有的窗口调节策略中新增了快速恢复策略,这样 \(TCP\) 发送端能更准确地计算链路中的包数目。\(Reno\) 避免了网络拥塞不够严重时采用慢启动形成的过分减少发送窗口的现象。
流程:当发送方收到 3 个连续的重复 \(ACK\) 确认包时,并使用快速恢复策略来代替慢启动策略,将 \(ssthresh\) 设置为当前拥塞窗口大小的一半(但要大于或等于2个 \(MSS\)),随后将拥塞窗口大小调节为 \(ssthresh+3\times MSS\)(当收到3个重复 \(ACK\) 时,依旧采起拥塞避免阶段每次加 1 个 \(MSS\) ,因此变为 3倍 \(MSS\)),重传丢失的数据包,此后每收到1个依旧重复 \(ACK\) 确认包时,也发送一个新的数据包,直到收到非重复 \(ACK\) 确认包后,将拥塞窗口大小设置为 \(ssthresh\) 并使用拥塞避免策略调节拥塞窗口。
优势:\(TCP-Reno\) 算法更加灵活的利用了 “数据包守恒” 的思想,在其新增的快速恢复策略中,每收到一个 \(ACK\) 确认包即认为链路中出现了 1 个数据包大小空缺,则发送一个新的数据包,使得 \(TCP\) 链接在恢复正常传输以前也能够传输适量的数据,下降了超时重传发生的概率,加快了 \(TCP\) 链接从网络拥塞中恢复的速度。
缺点:\(TCP-Reno\) 的快速恢复策略是针对一个窗口仅丢失一个数据包的状况来设计的,其快速恢复策略仅对首个丢失的数据包进行从新传输,在收到非重复的 ACK 确认包 后,即认为 \(TCP\) 链接数据传输已经恢复正常,若出现一个窗口连续多个数据包丢失,在连续收到多个重复确认时,拥塞窗口大小会被屡次减半,下降了 \(TCP\) 链接的链路带宽利用率。
\(TCP-NewReno\) 对 \(Reno\) 中的 "快速恢复" 算法进行了补充。它考虑了一个发送窗口多个数据包丢失的状况。当发送方收到3个连续的重复 \(ACK\) 确认包时,将最大的已经发送的数据包的序号记为 \(recover\) ,在收到序号大于 \(recover\) 的 \(ACK\) 确认包后才切换为拥塞避免策略。
优势:\(TCP-NewReno\) 保证了 \(TCP\) 链接可以从网络拥塞中正确恢复,进一步的提升了算法的性能。
带选择性重传策略的 \(SACK\)(\(select \ Acknowledgment\)) 机制一样也能够克服 \(Reno\) 算法中一个窗口丢失多个数据包时,\(TCP\) 的性能可能变得不好的问题。\(SACK\) 选项域包含一些 \(SACK\) 块,每一个 \(SACK\) 块报告已经收到或正在排队的非连续数据域。\(TCP\) 的接收端将 \(SACK\) 包传回给发送端通知它所收到的数据,这样发送端可选择仅重传丢失的数据。
优势:\(SACK\) 避免了没必要要的重传,减小了时延,提升了网络吞吐量。
缺点:在中等规模或大规模的 \(BDP\) (\(bandwidth-delay \ product\)) 网络,此时的 \(SACK\) 就像 \(DOS\) 攻击同样,每次遍历都要消耗大量的 \(CPU\),时间复杂度为O(n^2),n为 \(packets \ in \ flight\) 的数量。\(SACK\) 算法最大的缺点仍是在于修改协议 \(TCP\) 协议。
不管是 \(TCP-Tahoe、TCP-Reno\) 仍是 \(TCP-NewReno\) , 均为根据数据包的丢失状况推断网络拥塞程度的算法,这就致使 \(TCP\) 链接仅仅会在所处链路缓冲区没法容纳更多的数据包时才对自身发送速率进行控制,增长了网络的排队时延以及数据包丢失率。
\(TCP-Vegas\) 拥塞控制算法是根据最短 \(RTT\) 预测 \(TCP\) 链接的预期吞吐量 \(Excepted\) ,根据当前的 \(RTT\) 计算 \(TCP\) 的真实吞吐量 \(Actual\) ,并计算二者的差值,记为 \(diff\) ,若 \(diff\) 小于阈值 $\alpha $ ,则认为网络未发送拥塞,线性增长拥塞窗口大小;若 \(diff\) 超出阈值 $\beta $ ,则线性减少拥塞窗口大小;若 \(diff\) 处于二者之间,则拥塞窗口大小不变。
\[\text{指望吞吐量:}Expected=cwnd/BaseRTT \]\[\text{实际吞吐量:}Actual=cwnd/RTT \]\[\text{计算差值:}diff=\left( Expect-Actual \right) \times BaseRTT \]优势:因为 \(TCP-Vegas\) 并不彻底依赖于数据包丢失状况来检测网络拥塞,而是主要采用 \(RTT\) 的改变来判断网络的可用带宽,于是对网络拥塞程度的判断较为灵敏,有效的下降了数据包丢失率,在必定程度上提升了网络吞吐量。
缺点:\(TCP-Vegas\) 存在较大的 \(TCP\) 公平性问题,在与其它算法共存的状况下,基于丢包的拥塞控制算法会尝试填满网络中的缓冲区,致使Vegas计算的 \(RTT\) 增大,进而下降拥塞窗口的大小,使得传输速度愈来愈慢,所以一直未被普遍采用。
上述 \(TCP-Tahoe、TCP-Reno\) 和 \(TCP-NewReno\) 算法,使用了加性增长乘性减少 (\(Additive \ Increase \ Multiplicative \ Decrease, AIMD\)) 的拥塞窗口大小调整机制,即每一个 \(RTT\) 时间内拥塞窗口大小增加 \(\alpha\) (\(\alpha\) 的默认值为1),每当检测到数据包丢失时将拥塞窗口大小减少为 \(\beta\) 倍 (\(\beta < 1\) ,\(\beta\) 的值默认为0.5),会致使拥塞窗口大小须要经历不少个 \(RTT\) 才能使 \(TCP\) 链接的数据发送速度到达高带宽时延链路的承载上限,而一旦遇到数据包丢失则将拥塞窗口大小减半甚至重置为1个 \(MSS\) ,这使得拥塞控制窗口大小增加十分缓慢但减低过于迅速,浪费了大量的可用带宽。
咱们能够粗略估计一下 \(reno\) 算法在高速TCP环境下的处理,在一个 \(RTT\) 为\(100ms\),带宽为\(10Gbps\),包大小为\(1500bytes\)的网络中,若是满速率发送 \(cwnd\ge \left( 10\times 1000\times 1000\times 1000 \right) bit/s\times 0.1s/\left( 8\times 1500 \right) bit=83333\)。假设 \(cwnd=83333\) ,若是快速重传 \(cwnd\) 减半后从新进入拥塞避免状态,那么大约须要 $83333/2\times 100=4166666ms=4166ms=69\min $ 经过简单计算咱们能够发现,这种场景下大约须要69分钟,\(reno\) 的拥塞避免过程才能让 \(cwnd\) 足够大以充分利用 \(10Gbps\) 的带宽,显然69分钟的拥塞避免过程时间太长了,并且极可能会形成带宽浪费。实际上受限于丢包率,\(reno\) 的一个典型问题就是还没等 \(cwnd\) 增加到足够利用 \(10Gbps\) 的值就已经发生丢包并削减 \(cwnd\)了。
基于解决这一问题,\(Floyd\) 提出了 \(HSTCP\) 拥塞控制算法,其将拥塞窗口大小的函数 \(\alpha(cwnd)\) 和 \(\beta(cwnd)\) 做为 \(AIMD\) 机制中的参数 \(\alpha\) 和 \(\beta\) ,当拥塞窗口大小不超过预先设定的参数值 \(Low\_window\) 时,\(HSTCP\) 认为当前所处链路的时延带宽积较低,令 \(\alpha(cwnd)=1\) ,\(\beta(cwnd)=0.5\),与原有的拥塞控制窗口调节机制保持一致。(\(\alpha=a,\beta=b\))
![]()
当拥塞窗口大小大于 \(Low\_window\) 时,就会依据下列公式采起新的 \(a\) 和 \(b\) 来到达高时延带宽积的要求:
\[a\left( w \right) =w^2\times p\left( w \right) \times 2\times b\left( w \right) /\left( 2-b\left( w \right) \right) ,\ \text{其中}p\left( w \right) \text{时窗口为}w\text{时的丢包率} \]\[b\left( w \right) =\left( High\_Decrease-0.5 \right) \left( \log \left( w \right) -\log \left( W \right) \right) /\left( \log \left( W\_1 \right) -\log \left( W \right) \right) +0.5 \]\(High\_Decrease\) 是最大的乘性减少因子,标准TCP取值为0.5,\(HSTCP\) 取为0.1,\(W\) 为低窗口的临界值,也就是38,\(W\_1\)是窗口最大值,设为\(83000\)(为何是\(83000\)?Floyd是经过10Gbps,100ms的网络下计算出来的窗口值,精确值是\(83333\))。关于 \(a\) 和 \(b\) 在不一样拥塞窗口大小的取值,真正使用的时候能够经过查表的方式直接得出来。
\(HSTCP\) 同时也对慢启动策略指数增大拥塞窗口得方式进行了改进,在高\(BDP\) 网络上,原有的慢启动策略会以指数形式增长,越到后面拥塞窗口能够到达很大的值,从而致使 \(TCP\) 链接的传输速度增加至远超链路的缓冲区承载上限,形成大量数据包丢失。\(HSTCP\) 在拥塞窗口大小的指数增加过程当中设置了一个阈值 \(max\_ssthresh\) ,当窗口大小小于 \(max\_ssthresh\) 时,仍以指数形式增加,一旦超过 \(max\_ssthresh\) 后,令其在 \(RTT\) 时间内的增加不得超过 \(max\_ssthresh/2\) ,修改后的机制被称做限制慢启动 (\(Limited \ Slow \ Start\))。
优势:\(HSTCP\) 算法可以在高速网络中,不管是在高丢包率或低丢包率的环境下,都能达到很高的吞吐量。
缺点:与传统 \(TCP\) 拥塞控制算法共存时会存在公平性的问题。当 \(TCP-Reno\) 和 \(HSTCP\) 共享链路时(高速网络中且数据包丢失率较小的状况下) 在拥塞发生后,\(TCP-Reno\) 使用的拥塞窗口增加速率远远慢于 \(HSTCP\) 的增加速率,所以,通过若干次拥塞控制事件后,\(HSTCP\) 会不断地侵占传统 \(TCP\) 的可用带宽。
关于 \(TCP-BIC\) 和 \(TCP-CUBIC\) 算法的详细内容,能够参考个人另一篇博客,这里再也不详述。
其中关于 \(TCP-BIC\) 算法公平性问题,以下图所示,因为 \(TCP-BIC\) 是在收到 \(ACK\) 确认包时对拥塞窗口进行调节的,这样在 \(RTT\) 不一样的两个 \(TCP\) 链接竞争同一瓶颈链路时,\(RTT\) 较大的 \(TCP\) 链接因为收到的数据包频率始终小于 \(RTT\) 较小的 \(TCP\) 链接,其拥塞窗口 调节频率较低,增加较慢,致使 \(TCP-BIC\) 存在很严重的 RTT 公平性问题 。
![]()