Linux TCP拥塞控制算法原理解析
这里只是简单梳理TCP各版本的控制原理,对于基本的变量定义,可以参考以下链接:
TCP基本拥塞控制http://blog.csdn.net/sicofield/article/details/9708383
TCP中RTO计算http://www.tuicool.com/articles/Yn6vEr
TCP拥塞控制名词解释:
1.awnd(advised window) 通告窗口,由接收端tcp发送给发送端tcp,告诉发送端自己能用于接收新的数据包的当前可用空间。
2.cwnd(congestion window)拥塞窗口,人为引入的变量,用于拥塞控制。因为如果单独使用awnd,每次都按接收端最大窗口发送易引发网络的瞬时拥塞瞬时进入拥塞避免剧烈降低网络利用率。
3.ssthresh(slow start thresh)慢启动阈值,用于确定使用慢启动算法还是拥塞避免算法。当前窗口小于ssthresh的时候,使用慢启动算法按指数增加窗口;当前窗口等于ssthresh时,使用慢启动或拥塞避免算法增长发送窗口都可以;当前发送窗口大于ssthresh时,使用拥塞避免算法,按线性增加发送窗口。
4.发送窗口W = min(cwnd, awnd)
5.duplicate ACK重复ACK,TCP接收端在接受到错序/失序数据包时应该立即向发送端返回重复ACK。如接收端已经顺序收到1000号前的包,返回发送端1001,然后接收端接着收到了1002号包,而不是期望的1001号包,则其立即返回给发送端1001的ACK包,这个ACK相对于第一次的1001ACK就是重复ACK包。
原始版TCP协议--TCP-Tahoe:
1.慢启动
初始值cwnd=1(linux3.0之后是10),ssthresh初始值可以被设置为任意大(可以设置为awnd或更大,这样总是使TCP从慢启动算法开始,而不是拥塞避免算法),如linux 3.2.12是int最大值0x7fffffff.
该阶段,每个rtt周期发送窗口W加倍。时间曲线图中表现为指数曲线。
2.拥塞避免
如果出现丢包,由于TCP无法确认丢包类型,所以就认为发生了网络拥塞,重传包并进入拥塞避免算法。操作是:
ssthresh = max(flight size/2, 2*SMSS),flight size为当前发送窗口中已经发送但还没有收到ACK的包个数,即flight size<=W,SMSS为发送端最大分组大小。
cwnd=1(或其他初始值,如10),然后就可以重新从慢启动算法增加发送窗口。
3.丢包,重现慢启动
在上一步的基础上,根据新的初值,回到第1步重新执行慢启动。
如下图:
说明:
1.tcp启动时,cwnd=1,ssthresh=16,时间轴0-4是慢启动算法阶段。然后发送窗口增加到ssthresh时,进入拥塞避免阶段,时间轴4-12.
2.发送丢包时,cwnd重新置为1,ssthresh设置为当前拥塞窗口(24)的一半,即12.时间轴上12-13.
3.上一步设置完新的cwnd和ssthresh后,重新进入第1步进行执行慢启动算法。
后期Tahoe版本也引入了快重传算法(原始检测到丢包需要等到rto超时才能重传,快重传就是连续收到3个ack就立即重传)。
快恢复版本TCP--TCP-Reno(增加快重传/快恢复算法):
Tahoe版本检测丢包只能通过RTO超时依然收不到ACK时才能开始重传,Reno版的修改是,引入快重传机制:当收到对方连续3次重复ACK时,不必再等待RTO超时,认为网络已经发生拥塞丢包,立即重传数据包。同时,因为RTO时间内连续收到3次ACK,认定网络状况依然良好,丢包可能是网络发生了瞬时拥塞。所以不必对发送窗口进行过度调整。
快重传机制(Fast Retransmit):
当收到3次连续的重复ACK时,立即重传数据包,不必等待RTO超时。
注意:快重传不必一定跟快恢复算法同时使用。Tahhoe版本中也可以使用快重传,但cwnd依然调整为1,而不是快恢复算法的cwnd/2.
窗口示例:
快重传/快恢复算法(Fast Recovery):
1.收到3次重复ACK,设置ssthresh=cwnd/2。
2.重传丢失的包,设置cwnd=ssthresh+3。3是根据网络“数据包守恒”定律,即根据ACK机制,收到3个重复ACK,说明有3个数据包已经离开了网络,所以cwnd+3。
3.每收到一个额外的重复ACK(包5),cwnd=cwnd+1。只是为了反应出一个包已经离开网络的事实。
4.传输包
5.如果收到新数据包的ACK(包7或包9),cwnd=ssthresh,称为“缩小窗口”,退出快恢复阶段,进入拥塞避免阶段。
对应下图时间轴12-13.
总结:
TCP拥塞控制算法 RFC文档链接http://www.faqs.org/rfcs/rfc2581.html
新版本TCP--TCP NewReno(相对Reno少量但却重要的修改是对partial acknowledgment部分确认的处理):
Reno版本不足:
当发送窗口只有一个包丢失,则快重传后,一个rtt后收到的第一个ack会确认快重传启动之前全部已经发送的数据包。但是如果发送窗口中多个包丢失,那么快重传后第一个收到的ack只能确认发送窗口中的部分数据,称为“部分确认”。NewReno处理“部分确认”的方法是,认为那个包丢失并重传那个包。
NewReno新的快重传/快恢复算法:
1.当收到3次重复ACK,并且发送者并不处于快恢复过程时,
设置ssthresh = cwnd/2
记录recover=max sequence number,快重传算法之前已传输的最大的数据包序列号(包10或更大)。
2.重传丢失包(包5),设置cwnd=ssthresh+3
3.如果继续收到重复ACK(包5),cwnd=cwnd+1
4.发送包(由于上一步cwnd窗口扩张)
5.当收到新的数据包的ACK(包7或包9),这个ACK可能是对第2步重传或稍后的重传的确认。
if (ACK确认所有的从丢失包一直到并包括“recover”的数据包)
设置cwnd=ssthresh或min (ssthresh, FlightSize + MSS),然后退出快恢复阶段。
else
重传下一个没有被确认的数据包;将cwnd=cwnd-新确认的数据包个数+1,发送新的包。这样缩小窗口的目的是,当快恢复算法最终退出时,大约有ssthresh数量的数据包存在于网络中。如果收到新的ack,重复执行第3-4步。
总结:
TCP NewReno RFC文档链接http://www.faqs.org/rfcs/rfc2582.html
有理解错误的地方,欢迎指出。