参考文献: RFC2581, RFC3390
1. 前言
TCP是具备流控和可靠连接能力的协议,为防止TCP发生拥塞或为提高传输效率,在网
络发展早期就提出了一些相关的TCP流控和优化算法,而且也被RFC2581规定是每个
TCP实现时要实现的。
本文中,为求方便把将“TCP分组段(segment)”都直接称为“包”。
2. 慢启动(slow start)和拥塞避免(Congestion Avoidance)
慢启动和拥塞避免是属于TCP发送方必须(MUST)要实现的,防止TCP发送方向网络传入大量的突发数据造成网络阻塞。
先介绍几个相关参数,是在通信双方中需要考虑但不在TCP包中体现的一些参数:
拥塞窗口(congestion window,cwnd),是指发送方在接收到对方的ACK确认前向允许网络发送的数据量,数据发送后,拥塞窗口缩小;接收到对方的ACK后,拥塞窗口相应增加,拥塞窗口越大,可发送的数据量越大。拥塞窗口初始值的RFC2581中被规定为不超过发送方MSS的两倍,而且不能超过两个TCP包,在RFC3390中更新了初始窗口大小的设置方法。
通告窗口(advertised window,rwnd),是指接收方所能接收的没来得及发ACK确认的数据量,接收方数据接收后,通告窗口缩小;发送ACK后,通告窗口相应扩大。
慢启动阈值(slow start threshold, ssthresh),用来判断是否要使用慢启动或拥塞避免算法来控制流量的一个参数,也是随通信过程不断变化的。
当cwnd < ssthresh时,拥塞窗口值已经比较小了,表示未经确认的数据量增大,需要启动慢启动算法;当cwnd > ssthresh时,可发送数据量大,需要启动拥塞避免算法。
拥塞窗口cwnd是根据发送的数据量自动减小的,但扩大就需要根据对方的接收情况进行扩大,慢启动和拥塞避免算法都是描述如何扩大该值的。
在启动慢启动算法时,TCP发送方接收到对方的ACK后拥塞窗口最多每次增加一个发送方MSS字节的数值,当拥塞窗口超过sshresh后或观察到拥塞才停止算法。
启动拥塞避免算法时,拥塞窗口在一个连接往返时间RTT内增加一个最大TCP包长度的量,一般实现时用以下公式计算:
cwnd += max(SMSS*SMSS/cwnd, 1) (2.1)
SMSS为发送方MSS。
TCP发送方检测到数据包丢失时,需要调整ssthresh,一般按下面公式计算:
ssthresh = max (FlightSize / 2, 2*SMSS) (2.2)
其中FlightSize表示已经发送但还没有被确认的数据量。
3. 快速重传(fast retransmit)和快速恢复(fast recovery)
TCP接收方收到错序的TCP包时要发送复制的ACK包回应,提示发送方可能出现网络丢包;发送方
收到连续3个重复的ACK包后启动快速重传算法,根据确认号快速重传那个可能丢失的包而不必等
重传定时器超时后再重传,普通的重传是要等到重传定时器超时还没收到ACK才进行的。这个算
法是TCP发送方应该(SHOULD)实现的,不是必须。TCP发送方进行了快速重传后进入快速恢复阶段
,直到没再接收重复的ACK包。
快速重传和快速恢复具体过程为:
1. 当收到第3个重复的ACK包时,ssthreh值按公式2.2重新设置;
2. 重传丢失的包后,将拥塞窗口cwnd设置为sshresh+3*SMSS,人工扩大了拥塞窗口;
3. 对于每个接收到的重复的ACK包,cwnd相应增加SMSS,扩大拥塞窗口;
4. 如果新的拥塞窗口cwnd值和接收方的通告窗口值允许的话,可以继续发新包;
5. 当收到下一个ACK确认了新数据时,将cwnd大小调整为sshresh,减少窗口;对接收方
来说,接收到重发的TCP包后就要发此ACK确认当前接收的数据。
4. 结论
这些算法重点在于保持网络的可靠性和可用性,防止网络阻塞造成的网络崩溃,是相对
比较保守的。
5. 附录讨论
A君: 这些算法都是针对通信双方的事, 但如果从开发防火墙等中间设备的角度来看,
中间设备有必要考虑这些么?
端木: 这个...我好象也看不出必要性,因为算法的参数都是在双方内部而不在TCP数据包
中体现...但应该会让中间设备轻松点,这个就象在马路开车,这些算法就是交规
让你开得规矩点,交警只关心你开车的情况,而不管你开的是什么车,开得好交警
也轻松。好车可以让你很容易开好,但差车也可以开好。
A君: 这些算法原型提出也很早了, 最早是88年的事, 当时网络都处于初级阶段, 有个
9600bps的猫就很牛了, 计算机性能也很差, 因此实施这些算法还有点用; 但现
在过了快20年了, 百兆都快淘汰, 千兆, 万兆网络都快普及了, 即使PC机的内存
也都上G了,再规矩这种几K级别的数据量有意思么? 就好象现在喷气式战斗机都到
第4代了, 再研究螺旋桨战斗机还有意思么?
端木: 这个...这个就象病毒库了, 里面不也有无数的DOS时代的病毒, 你以后这辈子估计
都见不着的,但没有哪个防病毒厂商会把这些病毒从库中剔除,库是只增不减的。
有这么个东西也是一样,正因为平时没用,谁也不注意,知道了就可以吹一吹,
尤其拿去唬唬人是很有效的!
A君: 你真无聊!
端木: You got it! 不无聊干吗写博客啊!
端木: 搞技术有时候是很悲哀的一件事,必须牵扯七大姑八大姨的很多老东西,也就是向下
兼容,到一定程度将成为进一步发展的最大障碍,讲一个从smth看到的不是笑话
的笑话:
现代铁路的铁轨间距是4英尺8点5英寸,铁轨间距采用了电车轮距的标准,而电车轮距
的标准则沿袭了马车的轮距标准。
马车的轮距为何是4英尺8点5英寸?原来,英国的马路辙迹的宽度是4英尺8点5英寸。
如果马车改用其他尺寸的轮距,轮子很快就会在英国的老马路上撞坏。
英国马路的辙迹宽度又从何而来?这可以上溯到古罗马时期。整个欧洲(包括英国)的老路都是罗马人为其军队铺设的,4英尺8点5英寸正是罗马战车的宽度。
罗马战车的宽度又是怎么来的?答案很简单,它是牵引一辆战车的两匹马的屁股的总宽度。
段子到这里还没有结束。美国航天飞机的火箭助推器也摆脱不了马屁股的纠缠———火箭助推器造好之后要经过铁路运送,而铁路上必然有一些隧道,隧道的宽度又是根据铁轨的宽度而来。代表着尖端科技的火箭助推器的宽度,竟然被两匹马的屁股的总宽度决定了。