TCP/IP学习笔记:TCP拥塞控制
简介
拥塞指的是
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏。这种情况就叫拥塞(congestion)。
TCP模块任务:提供网络利用率,降低丢包率,保证网络资源对每条数据的公平性。---- 拥塞控制
标准文档:RFC 5681,介绍了拥塞控制4个部分:慢启动(slow start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)和快恢复(fast recovery)。
拥塞控制for linux算法实现,有多种:reno算法、vegas算法、cubic算法等。部分或全部实现。
查看方式(for Ubuntu 14):
$ cat /proc/sys/net/ipv4/tcp_congestion_control
cubic
我的PC是采用cubic算法。
术语介绍
拥塞控制最终受控变量:发送窗口(SWND,Send Window)。也就是说,拥塞控制算法最终是通过控制SWND大小,来进行拥塞控制的。
发送窗口
在发送端发送缓存,存在4种类型数据,其中,发送窗口SWND是指(2)和(3)
(1)已经发送并且对端确认(Sent/ACKed)---------------发送窗外 缓冲区外
(2)已经发送但未收到确认数据(Sent/UnACKed)----------发送窗内 缓冲区内
(3)允许发送但尚未发送的数据(Unsent/Inside)---------发送窗内 缓冲区内
(4)未发送暂不允许(Unsent/Outside)-----------------发送窗外 缓冲区内
发送缓存4种类别数据的示意图
接收窗口
接收窗口 指的是TCP报文头窗口字段,用于告诉发送端自己当前接收缓存大小。
发送者最大段大小 SMSS
MSS(Maximum Segment Size) TCP最大报文段长度(RFC 879),指的是每一个TCP报文段中的数据字段的最大长度。
TCP报文段 = 数据字段 + TCP首部(20~40Byte)
MSS = MTU - sizeof(TCPHDR) + sizeof(IPHDR)
MTU:最大传输单元(含TCP头部、IP头部);TCPHDR:TCP报文头;IPHDR:IP报文头。
MSS与MTU含义见下图:
发送者最大段大小,称为SMSS(Sender Maximum Segment Size)。
如何通告MSS?
MSS的默认值是536byte,因此对端应能接受报文段长度是536 + 20(TCP固定首部) = 556byte的TCP报文。如果想要改变MSS,就需要在TCP连接时,通过TCP报文头可变长的选项(option)字段(1byte类型+1byte长度+不定长内容),告知对端。
注意:MSS是设置好值后,再通告对方,而非与对方协商。
SWND大小的影响
如果SWND太小,会引起明显的网络延迟;反之,如果SWND太大,则容易导致网络拥塞。
既然接收窗口RWND可以控制发送窗口,为何还需要拥塞控制,而不直接用接收窗口进行控制?
虽然接收方可以通过接收通告窗口(RWND),来控制发送端SWND。但是接收方并不知道网络拥塞情况,无法针对网络情况进行控制。因此,发送端引入了一个称为拥塞窗口的(Congestion Windo, CWND)的状态变量。实际的SWND值是RWND和CWND中较小者。
慢启动和拥塞避免
慢启动
TCP连接建立OK后,CWND初值IW(Initial Window),大小2~4SMSS -- 发送端最多能发送IW bytes数据。
此后,发送端每收到接收端的一个确认,其CWND就按下式增加:
CWND += min(N, SMSS), 其中,N是此次确认中包含的之前未被确认的字节数 (1)
这样,CWND将按指数形式扩大,这就是慢启动。
慢启动算法理由:TCP模块刚开始发送数据时,并不知道网络的实际情况,需要一种试探的方式平滑地增加CWND的大小。
慢启动门限
如果不施加其他手段,慢启动必然使得CWND很快膨胀(慢启动并不慢),并最终导致网络拥塞。而慢启动门限(slow start threadhold size, ssthresh),就是这样一个限制:
当cwnd < ssthresh时,使用慢开始算法;
当cwnd > ssthresh时,停止慢开始算法,改用拥塞避免算法;
当cwnd = ssthresh时,既可以用慢开始算法,也可以用拥塞避免算法;
ssthresh初始值16。只要判断出现拥塞时,慢开始门限设为出现拥塞时发送窗口swnd的一半(但不能<2),然后把拥塞窗口cwnd重新设为1,执行慢开始算法。
目的:迅速减少主机发送到网络中的分组数,使得发生拥塞时路由器有足够时间把队列中积压的分组处理完毕。
拥塞避免算法
CWND按线性方式增加,从而减缓其扩大。RFC 5681提到2种实现方式:
1)每个RTT时间内,按(1)式计算新的CWND,而不论该RTT时间内发送端收到多少个确认。
2)每收到一对新数据的确认报文段,就按式(2)来更新CWND。
CWND += SMSS * SMSS / CWND (2)
下图粗略地描述了慢启动和拥塞避免发生的时机和区别。此外,我们假设当前ssthresh = 16SMSS大小(实际远不止这么大)
慢启动和拥塞避免,都是发送端在未检测到拥塞时,所采用的积极拥塞避免的方法。下面介绍拥塞发生时(可能发生在慢启动阶段或拥塞避免阶段)拥塞控制的行为。
发送端如何判断拥塞发生?
- 传输超时,或者说TCP重传定时器溢出; ---- 拥塞避免
- 接收到连续3个重复的确认报文; ---- 快速重传,快速恢复
拥塞控制对这两种情况有不同的处理方式。第一种情况,仍然使用慢启动和拥塞避免;第二种情况,则使用快速重传和快速回复(如果真的发生拥塞)。
如果发送端检测到拥塞发生是由于传输超时,即上述第一种情况,那么它将执行重传并做如下调整:
ssthresh = max(FlightSize / 2, 2 * SMSS) (3)
CWMD <= SMSS
其中,FlightSize是已经发送但未收到确认的字节数。这样调整后,CWMD将小于SMSS,那么必然小于新的慢启动门限值ssthresh(根据式(3),ssthresh一定不小于SMSS的2倍),故而拥塞控制再次进入慢启动阶段。
何为TCP超时重传?
TCP服务必须能够重传超时时间内未收到确认TCP报文段。为此,TCP模块为每个TCP报文段都维护一个重传定时器,该定时器在TCP报文段第一次被发送时启动。如果超时时间内未收到对方的应答,TCP模块将重传TCP报文段并重置定时器。
所谓超时,是指定时器 > 最大往返时间RTT。
RTT是指一个数据报发送到目的地,然后到发送方收到确认所需的时间,这是测量RTT。发送报文和确认报文并非数量上的一一对应,可能发送多次,确认一次,也可能是一一对应。
快速重传和快速恢复
如何通过接收到重复的确认报文段,判断网络是否真的发生拥塞?
发送端接收到重复的确认报文段可能情形:TCP报文段丢失,接收端收到乱序TCP报文段并重排等。
拥塞控制算法需要判断当收到重复的确认报文段时,网络是否真的发生了拥塞,或者说TCP报文段是否真的丢失了。具体做法:
发送端如果连续收到3个重复的确认报文段,就认为拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞,过程如下:
1)当收到第3个重复的确认报文段时,按(3)式计算ssthresh,然后立即重传丢失的报文,而不是等到重传计时器超时,这称为快速重传。并按式(4)设置CWND
CWND = ssthresh + 3 * SMSS (4)
2)每次收到1个重复的确认时,设置CWND = CWND + SMSS,而非从慢开始算法的cwnd=1开始。此时,发送端可以发生新的TCP报文段(如果新的CWND允许的话)
3)当收到新数据的确认时,设置CWND = ssthresh(ssthresh是新的慢启动门限值,由第一步计算得到)
步骤1)称为快速重传,步骤2)3)称为快速恢复。快速重传和快速恢复完成之后,拥塞控制将恢复到拥塞避免阶段。这点由第3)步操作可得知。
总结
拥塞控制对象:发送窗口SWND. SWND <= min(RWND, CWND)
网络出现超时 => 采用慢开始算法 + 拥塞避免算法;
网络出现重传 => 快速恢复算法;
参考
[1]谢希仁. 计算机网络.第5版[M]. 电子工业出版社, 2008.
[2]游双. Linux高性能服务器编程[M]. 机械工业出版社, 2013.
[3]Postel J . Transmission control protocol; rfc793[J]. Rfc, 1981.(RFC793)
[4]Тезисы, Статусдокумента, Авторскиеправа, et al. RFC 5681 TCP Congestion Control. 1996.(RFC5681)