TCP的拥塞控制 (三)
1. Multiple Packet Losses
Fast Retransmit/Fast Recovery机制可以很好地处理单个packet丢失的问题,但当大量packet同时丢包时(一个RTT内),FR机制可能无济于事。
下面举例说明。
下面两张图是一次多包丢失的情景。其中上图中的三条线分别描述的是SND.UNA,SND.NXT,SND.UNA+SND.WND三个变量随着时间变化的情况,下图中的线描述的是拥塞窗口cwnd随着时间变化的情况。
由于丢包具有随机性,我们假设下面的情景开始之前,小于65号的包都已经被sender发送出去,但51, 53, 55号在网络中被丢弃。
在0.83s之前,sender正常的发送packet,并且收到了前面发送的packet的ACK,因此cwnd也在线性增加。但是由于这个阶段发送窗口太大,大量的packet被同时投递到网络中,实际上已经导致了网络拥塞,大量包被丢弃(由于丢包具有随机性,我们假设50号之后的奇数号包被丢弃),在0.83s才被sender察觉到。此时sender收到packet51的3个重复ACK,按照算法会进入Fast Recovery阶段,即重传packet51,并将cwnd和ssthresh减半。
0.83s之后,sender仍然会继续收到重复ACK,cwnd会线性增加。在0.96s时刻,cwnd恢复到了Fast Recovery之前的值,之后虽然有新的packet进入拥塞窗口,但超过了发送窗口的范围,因此仍然不被发送。在1s时刻,sender收到了响应重传packet51的ACK,按照算法,cwnd减为ssthresh,并且发送窗口向前滑动,一个新的packet进入发送窗口,但由于该packet超过了拥塞窗口的范围,因此仍然不被发送。
1s之后,packet51之后的packet都无法得到响应。本应用于响应这些packet的ACK被作为packet51的重复ACK,并且由于sender再无法发送新的packet,也就不能产生重复ACK,因此,packet51之后的packet再也无法得到响应。此时,网络处于干涸状态(dryout)。最终,只有等到这些packet超时。
在1.5s时刻,packet53超时,触发重传,并且进入Slow Start阶段。之后,整个网络又从0开始,严重影响网络性能。
导致这种现象的原因是不恰当的初始窗口大小,也即ssthresh的值。在早期阶段,由于sender的实际发送窗口较大,其在一个RTT时间内,将发送窗口内的packet一次性投送到网络上,导致了网络上的某些路由器队列溢出,大量的packet被丢弃。
2. Damped Slow Start
由于receiver端空洞的存在,每当receiver填充完一个空洞时,就会回复一个同时响应一大块区域的ACK,这会导致sender端的发送窗口突然增大,会有导致网络拥塞的风险。
同样看上面的例子。由于多个包被丢失,在receiver端产生了一个个的空洞。例如,51, 53, 55号包被丢弃,其他的包被reciever正常接收。
在1.5s时刻时,packet53超时导致重传,receiver收到packet53之后,填入缓冲区的空洞中,并使用1个ACK响应竟可能大的连续区域,即packet53, 54。
在1.61s时刻,sender收到了一个ACK,将发送窗口向前滑动,并按照算法将cwnd+1。并将窗口里的packet55, 56发送出去。
在1.73s时刻,sender收到了一个同时响应packet55, 56的ACK,将发送窗口向前滑动,并将cwnd+1。
在1.74s时刻,sender收到了一个响应packet56的ACK,将发送窗口向前滑动,并将cwnd+1。此时,packet56实际上有两个ACK。
图 2 窗口激增
3. False Fast Retransmit
当超时或者收到3次重复ACK时,发送窗口会缩减,许多原本在发送窗口之中的packet离开了发送窗口。当Fast Retransmit结束后,发送窗口又重新开始增长。这会导致之前已经发送出去的packet重新进入发送窗口,被再次发送。重复发送receiver端已有的数据包,不但浪费带宽,也会导致另一个问题: False Fast Retransmit。下面举例说明。
假设TCP处于Slow Start阶段。packet58, 59, 60已经在receiver端缓存,当前发送窗口大小为2,起始为packet55。
sender将packet55, 56发送出去,收到ACK之后,发送窗口增大为4,并滑动到packet57,此时,packet57, 58, 59, 60位于发送窗口中,被发送出去。
receiver在收到packet57后,采取累积确认的方式,在ACK中指明packet61。
sender收到packet57的ACK之后,发送packet61。之后,sender又陆续收到packet58, 59, 60的ACK,均指明packet61,这样,就收到了3次重复ACK,导致packet61的重传。
实际上,此时packet61并没有丢包,但sender无法意识到这一点,仍然按规则进行了Fast Retransmit,这就是False Fast Retransmit。
核心问题是,sender无法区分这两种情况导致的3次重复ACK:真正的丢包产生的,以及重复发送receiver端已缓存的包产生的。那么为什么会有重复发送receiver端已缓存的包的情况存在呢?这是由于在多包丢失时,sender端是无法知道具体哪些包丢失的。因为第一批到来的ACK指明的都是第一个丢失的packet,而没有其他丢失的packet的信息。因此,sender要么采取保守的策略,认为只有一个packet丢失,从而重传ACK中指明的那个packet,对于其他丢失的packet,此时已经没有可用的重复ACK了,故只能等到超时的到来才有机会重传。sender要么采取更积极的策略,一次重传多个packet。重传多个packet虽然能够覆盖所有的丢失的packet,但也会一并重传已经被receiver端正常接收并缓存的packet。这就是为什么会有重复发送receiver端已缓存的包的情况存在。
4. Multiple Fast Retransmits
由于False Fast Retransmit时常发生在Fast Recovery阶段,因此,算上Fast Recovery阶段传统的Fast Retransmit,如果又发生了False Fast Retransmit,那么就产生了另外一个问题Multiple Fast Retransmits,即一个窗口内,进行了多次Fast Retransmit,导致窗口迅速减小,影响TCP的性能。
可以看出,Multiple Fast Retransmits虽然不影响TCP的正确性,但会影响其性能,所以消除Multiple Fast Retransmits有一定的意义。可以通过消除False Fast Retransmit来达到这一目的。实际上,从上文已经知道,False Fast Retransmit并不是必须的,其所重传的packet会在Fast Recovery阶段的其它步骤中发送出去,因此,直接消除False Fast Retransmit并不会影响TCP的正确性。
那么现在需要考虑的是如何消除False Fast Retransmit。我们知道,在Fast Recovery阶段,触发False Fast Retransmit的重复ACK,与触发Fast Retransmit的重复ACK,回复的都是同一个窗口内的packet,因此,只要在Fast Retransmit之后避免同一个窗口内其他Fast Retransmit即可。具体方式是,进入Fast Recovery时,标记当前窗口的最大序号high_seq,后续若再次收到3次重复ACK,则判断其序号是否在当前窗口内(比较与high_seq的大小)。如果是,则忽略这3次重复ACK,不进行Fast Retransmit,否则会导致Multiple Fast Retransmits;如果不是,则进行Fast Retransmit,因为这是下一个窗口的packet,不应算在当前窗口内。
5. Partial Acknowledgement
当收到3次重复ACK时,sender进入Fast Recovery阶段,重传ACK中指明的packet,之后,当响应这个重传的packet的ACK到来时,会携带receiver端最新的信息,其中包括所等待的下一个packet。
如果当前发送窗口只有一个packet丢失,那么这个ACK中指明的即为已接收到的最大的packet序号,它一并回复了重传之前sender已发送的所有packet。
例如,sender的发送窗口为[51, 54],将所有packet发送出去之后,packet51丢失, sender会收到3次重复ACK,进而重传packet51,之后,sender会收到重传的packet51的ACK,其中指明packet55。
而如果当前发送窗口有多个packet丢失,那么这个ACK指明的就不是最大的packet序号,它仅仅回复了sender所发送的部分packet。这个ACK就称为Partial Acknowledgement。
例如,sender的发送窗口为[51,58],将所有packet发送出去之后,packet51, 54, 55丢失,sender会首先收到指明了packet51的3次重复ACK,进而重传packet51,之后,收到packet51的ACK,其中指明的是packet54,因为packet54是receiver端未收到的最小的packet。这个ACK指明的不是packet59,因此没有回复sender发送的所有的packet。
传统TCP认为一个窗口内只有一个packet会丢失,因此每次Fast Retransmit只会重传一个packet。当多个packet丢失时,receiver端响应的所有ACK都指明了丢失的最小的packet,而不会指明其他丢失的packet,这就导致只有丢失的最小的packet会被重传,其他丢失的packet会被隐藏,只有等到超时才能重传。然而,重传丢失的最小的packet之后,响应它的ACK实际上是一个Partial Acknowledgement,它指明了第二个丢失的packet,可以借此重传其他丢失的packet,而无需等到超时的发生。这就是Partial Acknowledgement的作用。
对于sender如何处理Partial Acknowledgement,还有一些问题需要考虑。
首先是每当收到一个Partial ACK后,该重传多少个packet?也即发送窗口的大小设置为多少?
一种保守的策略是只重传一个packet,即Partial ACK中指明的packet。这种方式不会导致重传receiver端已经缓存的packet,从而避免了False Fast Retransmit。
另一种更快速的策略是Slow Start,每当收到一个Partial ACK,就增加发送窗口,这样可以更快地覆盖所有丢失的packet,但会导致很多已经被receiver端缓存的packet被重复发送。
进一步,如果采取第二种策略,该如果解决False Fast Retransmit的问题?实际上,我们已经在Multiple Fast Retransmit中讨论了这个问题。只需在收到同一个窗口里的重复ACK之后,忽略掉即可。
参考文献
[1] Braden, R. "RFC 1122." Requirements for Internet Hosts—Communication Layers (1989).
[2] Postel, Jon. "RFC 793: Transmission control protocol, September 1981." Status: Standard (2003).
[3] Jin, Cheng, David X. Wei, and Steven H. Low. "FAST TCP: motivation, architecture, algorithms, performance." INFOCOM 2004. Twenty-third AnnualJoint Conference of the IEEE Computer and Communications Societies. Vol. 4. IEEE, 2004.
[4] Jin, Cheng, et al. "FAST TCP: From theory to experiments." Network, IEEE 19.1 (2005): 4-11.
[5] Clark, David D. "RFC 813: Window and acknowledgement strategy in TCP, July 1982." Status: UNKNOWN.
[6] Jacobson, Van. "Congestion avoidance and control." ACM SIGCOMM Computer Communication Review. Vol. 18. No. 4. ACM, 1988.
[7] Hoe, Janey C. Start-up dynamics of TCP's congestion control and avoidance schemes. Diss. Massachusetts Institute of Technology, 1995.
[8] Nagle, John. "RFC 896: Congestion control in IP/TCP internetworks." (January 1984) (1984).
[9] Allman, Mark, Vern Paxson, and William Stevens. "RFC 2581: TCP congestion control." (1999).
[10] Allman, Mark, and Vern Paxson. "RFC 5681: TCP congestion control." (2009).
[11] Allman, Mark, and Vern Paxson. "RFC 6298: Computing TCP's Retransmission Timer." (2011).
[12] Stevens, W. "RfC 2001: TCP Slow Start." Congestion Avoidance, Fast Retransmit, and Fast Recovery Algorithms 1 (1997).
[13] Floyd, S., and T. Henderson. "RFC 2582-The NewReno Modification to TCP’s Fast Recovery Algorithm, april 1999." Status: PROPOSED STANDARD: 115.
[14] Floyd, S., and T. Henderson. "RFC 3782: The NewReno Modification to TCP's Fast Recovery Algorithm, IETF." (2004).
[15] Gurtov, Andrei, and Sally Floyd. "Resolving Acknowledgment Ambiguity in non-SACK TCP." Next Generation Teletraffic and Wired/Wireless Advanced Networking (NEW2AN’04) (2004).
[16] Floyd, Sally. TCP and successive fast retransmits. Technical report, October 1994. ftp://ftp. ee. lbl. gov/papers/fastretrans. ps, 1995.
[17] Mathis, M., Mahdavi, J., Floyd, S. and A. Romanow, "TCP Selective Acknowledgement Options", RFC 2018, April 1996.