网络拥塞成因与处理

网络拥塞成因与处理

引言

TCP 是最常用的传输层协议之一,为上层协议提供面向连接的、可靠的传输服务。为了尽可能的提供最大的吞吐量,TCP使用滑动窗口和拥塞窗口机制来控制某一时间点投放到网络中的数据量,已达到尽可能占满可用带宽,并且不产生拥塞的目的。网络中通常都会有一个瓶颈(bottleneck),数据包会在瓶颈处积压,甚至被丢弃。因此TCP流量控制的关键就是控制在瓶颈处数据包的积压程度,积压过快就会导致拥塞。拥塞是如何形成?哪些因素引起拥塞?发送方如何感知拥塞?如何缓解拥塞?本文将讨论这些问题。

Byte in flight

在展开本文之前,先介绍一个概念——Byte in flight 一个比较形象的翻译“在途字节数”1,指的是已经发送,但尚未收到ACK确认的字节数,约等于当前在网络中传输与积压的数据包的字节数的总和。Byte in flight 的大小直接影响瓶颈处数据包的积压程度。根据滑动窗口的概念可以知道Byte in flight的大小受限于发送窗口2(Byte in flight <= 发送窗口),

拥塞的形成

拥塞是如何形成的?看起来很简单的问题,实际上并不是所有人脑海中都有清晰的图景。概括来说,拥塞的主要成因是数据发送方投放到网络中的数据超出了网络的承载能力,用前文的概念就是Byte in flight 超出网络承载量。就像水管的流量取决于最细的部分,网络的承载能力取决于网络上可用带宽最小路径,也就是有瓶颈所在。网络设备接口往往都有缓冲区,超量的数据无法从瓶颈路径发送,只好在缓存区排队,而缓冲区的大小有限制,积压的数据量超出缓存区大小,就会发生丢弃,这个过程就像漏斗倒水一样
网络拥塞成因与处理_第1张图片

图1

产生拥塞的原因有:
网络中存在瓶颈,该瓶颈的吞吐量小于节点单位时间发送的数据量
接收缓冲空间大于瓶颈处可用缓冲空间

现实中的例子

为了更直观的展示拥塞的形成,这里举一个例子:假设有两个仓库,仓库A和仓库B,一河之隔,仓库A的经理需以最快速度将仓库A的货物搬运到仓库B,仓库到河流之间的大路比较宽,搬运工人可以并排行走,河流之上有一座桥,和大路一样,桥是双向的,但是这座桥比较窄,一个方向上,搬运工人只能单列行走,假设搬运工人是无限的。以上这种情况与TCP传输的情况很类似,桥梁就是传输路径上的瓶颈。搬运过程如下:
网络拥塞成因与处理_第2张图片

图2

1)理论上来说参与的工人越多,搬运的速度就越快,于是仓库A的经理与仓库B的经理沟通,希望尽可能的增加工人,但是仓库B的经理反对这种做法,理由是仓库B的卸货区(如图2——②)大小有限,只能容纳200箱子货物,搬运工人太多,叉车无法及时完成摆放,会造成货物堆积,搬运工人数量应小于200。这里的卸货区容量就对应TCP的接收窗口。
2)仓库A经理派出200名搬运工人,心想这下没问题了,200名工人肯定不会造成仓库B货物积压。但事不如人愿,刚开始搬运不一会就有很多工人浑身是水抱着浸水的货物回来,重新打包(丢包重传)。原来参与搬运的工人太多,桥梁的通过能力不济,大部分工人在河岸边排队,排队区(图2-①)只能容纳50人,排队的人太多,有不少人因为过于拥挤被挤下水(如图2-③)。这里面200名工人相当于发送窗口,排队区就相当于瓶颈处的可用缓存空间。
3)仓库A经理将搬运工人减少到100人,此时不再有人被挤下水,搬运圆满完成。

上述2)中,经理一次性派出200名工人出发,假如路途足够远(最后一名工人离开后,第一名工人还没有回来),200名工人都会在路途中,因此200此时也相当于Byte(goods) in flight。由于无法直观的观察发送窗口的变化,可以通过Byte in flight 粗略观察。为了验证上述想法,我做了一个简单的实验,实验拓扑如下:
网络拥塞成因与处理_第3张图片

图3

两台路由器之间是20M专线(100km以上),因此两路由器之间的路径是整条传输路径的瓶颈。客户端通过FTP 上传(put)650M的文件到FTP server,使用tshark抓包并通过如下命令将所有的数据包的byte in flight值导出:

tshark -r "ctcp测试-650m.pcapng" -Y "tcp.analysis.bytes_in_flight"  -n -t r -V -T fields -e tcp.analysis.bytes_in_flight > byte_in_flight.txt

将得到的数组绘制成折线图如下:
网络拥塞成因与处理_第4张图片

图4

图4纵坐标是字节,横坐标数据包的序号。在tcp三次握手阶段,ftp服务器将接收窗口告知客户端,这个试验中接收窗口是10485760 Byte (10MB)。从图4 可以看到,传输开始阶段byte in flight 呈指数增长,试图尽快达接收窗口的大小。但当向网络中注入4000000Byte左右的数据时,发生了严重的拥塞,产生了大量超时重传,发送方将发送窗口急巨减少,以缓解拥塞,之后发送窗口以相对较慢的速度增长,(可能是斜线或对数曲线或其他曲线,不同的算法会有不同),从图4可以看出byte in flight也以类似的速度增长。
当byte in flight增长到1089160时(发送窗口此时也是),又发生了拥塞,这次只丢少量的数据包,触发了发送方快重传,发送窗口降到发生拥塞时未被确认的数据量的一半[RFC 5681],即1089160/2=544580,之后继续增长。

拥塞点(congestion point)

上文仓库的例子中可以看到,当经理将搬运工人的数量(发送窗口)降到排队区大小100人时,不再会发生拥塞,100这个值,我们定义为是路径中粗略的拥塞点,也就是网络中发生拥塞时的在途字节数就是该时刻的拥塞点,此时发送方发送的数据填满路径的带宽管道。可以推测,只要发送方保证任何时刻发送的未确认的数据量小于该时刻的拥塞点,就不会产生拥塞。真实的TCP传输过程中,发送方不大可能预知拥塞点的大小,并且拥塞点还不断的变化,因此发送方只能相对保守的试探,如图4 当发送方将窗口缓慢增大到一定值时(第306235包左右),再次发生拥塞,如果横坐标足够长,我们可以看到每次发送方将窗口增大到一个值时,还会再次发生拥塞,我们会发现这些值可大致连成一个与横坐标平行的直线,这个直线与纵坐标相交的值,也就是平均发生拥塞时的byte in flight,可以粗略认为是拥塞点。

当然估算拥塞点不需要这么麻烦,相应的计算方法,本文就不介绍了。这里介绍一下利用估算拥塞点,消除拥塞。原理很简单,确保发送方的发送窗口永远不高于拥塞点的值,就可避免拥塞,可以通过修改接收窗口等方法限制发送窗口。下图5是修改接收窗口到拥塞点以下时,byte in flight的变化趋势,用图4同样方法导出的数据:

网络拥塞成因与处理_第5张图片

图5

如图5 可以看发送方byte in flight 呈指数增长到接收窗口大小后,不再增长,发送窗口也同样。实践证明没有发生拥塞。后续byte in flight 变小,是因为及时收到了确认,发送窗口不变。使用该方法的确能减少拥塞,但不一定会增大吞吐量,反而会减少灵活性,使用时要注意验证。

缓解拥塞

基于丢包(loss-besed)

发送方感知到拥塞时,必须采取动作缓解拥塞,发送方如何感知拥塞的发生呢?一种方法就是根据丢包情况。像图2中一样,工人在桥边被挤下水,返回重新打包货物,此时经理便意识到发生拥塞了。可是网络传输过程中,数据包丢弃了不可能返回通知发送方,该如何发现丢包呢?TCP结合计时器和ACK机制来判断,简单来说TCP先预估发送方到接收方的往返时间,这个值称为SRTT(Smoothed Round Trip Time),根据预估往返时间计算出一个最大等待时间(RTO),当发送的数据在最大等待时间内未收到接收方确认(想想 搬运工人没有在规定的时间返回),就断定发生了丢包,必须重传,并且降低发送窗口,图4第一个谷底可以看到类似情景。关于RTO的计算方法很多,这里不详细介绍,请参照RFC-793、RFC-2988、RFC-6298等文献。

丢包发生就一定代表拥塞吗?考虑一下这种情况:如图2 假设所有货物都贴有编号,搬运工人按顺序搬运货物,一名搬运工人在过桥期间不小心掉下水,后续的工人正常将货物搬运到了仓库B,仓库B的经理入库货物时发现货物未按照顺序到达,便立即打电话通知仓库A经理,此时仓库A经理需要减少搬运工人吗?肯定是不需要的,因此为并没有发生拥塞。网络传输过程中也有类似现象,比如网线质量不好,导致一数据帧因为CRC校验错误被丢弃。接收方发现到达的数据乱序后,立即发送duplicate ack通知发送方数据包乱序了,当累积了三个duplicate ack 后发送方快速重传数据,不用等到超时重传。当然发送方为了保险,会主动减少发送窗口,但不会像发送超时重传那样大幅度减少,图4 第二个谷底可以看到类似情景。

超时重传对吞吐量的影响比较大,因为大量超时重传已明确表明网络瓶颈处发生了拥塞,发送方一般会将发送窗口降到一个相当低的值,重新缓慢增长发送窗口,以避免更严重的拥塞发生。高延迟的网络中发送窗口增长更加缓慢,少量的超时重传都会对吞吐量造成显著影响。因此多数TCP的拥塞避免算法会尽量避免超时重传,或减轻超时重传的影响。

基于延时(delay-besed)

基于丢包的拥塞避免有一个弊端是只有当拥塞发生以后发送方才能感知,然后采取动作。有没有办法在拥塞发生之前就使发送方提前感知,然后采取动作,从而避免拥塞后发送窗口大幅下降造成的吞吐量下降呢?
Brakmo和Peterson在1995年提出了一种使用RTT(Round Trip Time)预测拥塞的拥塞控制算法,称之为TCP Vegas。TCP vegas 里面的主要思想就是通过观察RTT的变化来判断当前网络是比较畅通还是比较拥堵,理由是持续增长的RTT被认为是发生拥塞的先兆。
怎么回事呢?回到图2,假设只有一名搬运工人搬运货物,这名工人在畅通的情况下将货物从仓库A搬运到仓库B,再从仓库B返回,往返时间(RTT)为5min,经理开始逐步增加工人,增加到一定程度后,工人就会在桥边排队,由于需要加上排队的时间,此时工人往返时间一定会大于5min,根据工人往返时间,经理就可以大致判断桥边等待队伍的长度,这里面引入几个值:
1)basertt:basertt 记录网络传输路径上的最小rtt,这里面就是5min;
2)srtt:平滑rtt,综合历史rtt和当前rtt计算出的值,计算公式这里不叙述了,为了简单,这里就理解为当前的rtt;
3)expected:网络理想状态下的吞吐量;
4)actual:当前真实的吞吐量;
当经理派出了100名工人,此时工人的往返时间增加到10min(srtt)。经理可以通过下面方法计算,桥边等待队伍的长度:

expected(throughput)=wnd(发送窗口)/basertt=100/5=20  
actual(throughput)=wnd/srtt=100/10=10  
diff=(expected-actual)*basertt=(20-10)×5=50 

diff就是队列长度,即50人。
TCP vegas 采用相同的方法计算瓶颈路径前的队列长度。并规定diff的上限下限,作为判断当前网络状况的参考。当diff 大于上限时发送方主动减小发送窗口,当diff 小于下限时,发送方适当增加发送窗口。

基于延时的弊端

根据前文的描述,理论上基于延时的拥塞避免算法可以真正的“避免”拥塞的发生,当发送窗口增长拥塞点附近时,RTT会增大,发送方会据此适当调整发送窗口,最终表现为发送窗口维持在拥塞点附近,在延时高的高速网络上吞吐量提升明显。可以估计基于延迟的TCP, 发送窗口与时间的变化关系会类似于下图:
网络拥塞成因与处理_第6张图片

图6

从图6可以看到,发送窗口一直维持在较高点,TCP可将带宽管道填满,一切看起来很美满。但事实真是这样吗? 当TCP vegas 和 TCP Reno (loss-besed)共同竞争瓶颈带宽时,会发生什么呢?当网络将要发生拥塞时,TCP vegas 感知到RTT增加后,主动减小发送窗口,此时TCP Reno 会继续增加发送窗口,最终结果是,TCP Reno 发送窗口一直在较高的位置震荡,TCP vegas 则一直维持着较低的位置。TCP vegas的窗口随时间变化 如图7。
两种TCP共同竞争时,TCP Reno 会有很大优势;当网络中通信方式都为TCP vegas 时,整体传输效率会优于 TCP Reno。
网络拥塞成因与处理_第7张图片
图7

折中方案

既然delay-besed 和loss-besed 拥塞避免机制都有缺点,能不能将二者结合在一起呢?实际上这种算法现在已经大规模应用了,从Windos 7 与 Windos Server 2008 开始,微软系统内核就默认采用一种全新的TCP——CTCP(Compound TCP),可在CMD使用命令

netsh interface tcp show global

查看,CTCP的核心设计理念是在传统TCP的loss-based拥塞避免算法基础上添加一个可伸缩的delay-based 组件,以期既能实现delay-based的算法的优点,并且在同传统TCP竞争瓶颈带宽时保证公平性。CTCP的算法比较复杂,以后再详细阐述。



  1. 出自《Wireshark 网络协议分析的艺术》; ↩
  2. 发送窗口,即TCP发送方维护的窗口,发送窗口=min[接收窗口,拥塞窗口],滑动窗口与拥塞窗口的算法本文不再详细介绍; ↩

你可能感兴趣的:(网络,TCP拥塞控制,TCP-Reno,TCP-Vegas,CTCP)