tcp 拥塞控制引擎&状态机

373 阅读19分钟

拥塞控制算法

名词

cwnd:是发送方维护的一个的状态变量,会根据网络的拥塞程度动态变化。变化规则:

只要网络中未出现拥塞,就增大cwnd;只要出现了拥塞,就减小cwnd

rwnd:接收窗口,接收缓存区大小

swnd:发送窗口,swnd = min(cwnd, rwnd),发送方能发送但未确认的帧数的上界。网络中实际传输的未经确认的数据

MSS:Maximum Segment Size,TCP一次传输发送的最大数据段长度

慢启动门限:ssthresh(slow start threshold)

DACK(重复确认)

SACK(选择性确认)

RTO(重传超时时间)

目的

防止过多的包被发送到网络中,避免出现网络负载过大

状态变量

每一个 TCP 连接需有2个状态变量:接收窗口rwnd和cwnd。

TCP 拥塞控制机制

包括慢启动、拥塞避免、快速重传、快速恢复4部分。 (拥塞控制算法是动态变化的)

启动条件

详细说明

备注

慢启动

cwnd<ssthresh

最开始:cwnd=1 MSS

增加过程:发送端每收到1个新的 ACK,将cwnd增加1 MSS,每经过1RTT,就将cwnd增大一倍。

判定发生拥塞的标准:超时重传

慢开始是指一开始向网络注入的报文段少,并非指cwnd增长速度慢

拥塞避免

cwnd>ssthresh

增加过程:每收到一个新ACK,只将 cwnd 增加1/cwnd

判定发生拥塞的标准:超时重传

并非能完全避免拥塞,而是指在此阶段将cwnd控制为线性增长,使网络较不容易出现拥塞

快速重传

在发送方收到3个Dup ACK立刻对DACK指示的报文重传,而不用等到TCP超时重传。而快速重传后不使用慢启动,而是拥塞避免,所以这又叫做快速恢复。

对于个别报文段丢失,发送方不会出现超时重传,也就不会误认为出现了拥塞从而降低cwnd

快速恢复

是在收到3个DACK时,将ssthresh减为原来一半,不执行慢启动,而是将cwnd 设为 ssthresh减半后的大小,进入拥塞避免阶段。

考虑到若网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能未出现拥塞

传统拥塞控制算法有4阶段

1.慢开始

由小到大逐渐增大cwnd(注:慢开始只是起点低,增加速度并不慢,指数增加)

阶段转变:当报文数目达到cwnd的初始门限时,由慢开始变为拥塞避免阶段

2.拥塞避免

cwnd缓慢线性增大

特点:是为了避免报文数增长过快导致丢包

说明:丢包无法避免,TCP是可靠的并非说不丢包,而是有重传机制可保证报文丢失后的再次传输

阶段转变:当出现第一个丢包时,触发快重传机制,重传完成后进入快恢复阶段

3.快重传

接收方收到失序报文段发出DACK,发送方收到3个DACK立即重传

为什么叫快重传,区别超时重传,超时重传是在发包时设置定时器,超过时间没收到返回的ACK就重传;而快重传是接收端发现收到的报文段失序后,连续收到2个相同报文即重传,不用等到定时器超时

若重传还是未传输成功是如何处理?

此处有2个标准一个是时间一个是次数,若在指定时间或次数内未重传成功会终结该链接。

4.快恢复

发送方收到3个DACK后慢开始门限减半

新cwnd=新慢开始门限(丢包时cwnd的一半)+3 (3个重传的报文段)

若收到新ACK,表明重传的包成功了,则退出快速恢复算法。将cwnd设置为ssthresh,然后进入拥塞避免算法。

阶段转变:重新确认cwnd大小后,将状态变更到拥塞避免阶段

拥塞控制算法的思路

TCP通过维护一个cwnd来进行拥塞控制

拥塞控制的原则是,只要网络中未出现拥塞,cwnd就可再增大一些,以便把更多的数据包发出去,但只要网络出现拥塞,cwnd的值就应减小一些,以减少注入到网络中的数据包数

TCP拥塞控制算法发展过程中出现了如下几种思路:

基于丢包

将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大cwnd,当出现丢包时,将cwnd减小,如Reno、Cubic等。

缺点

1)不能区分是拥塞导致的丢包还是错误导致的

基于丢包的拥塞控制方法把数据包的丢失解释为网络发生了拥塞,而假定链路错误造成的分组丢失是忽略不计的,这种情况是基于当时V. Jacobson的观察,认为链路错误的几率太低从而可以忽略,然而在高速网络中,链路错误不能忽略,该假设不成立。在无线网络中,链路的误码率更高,因此,如果笼统地认为分组丢失就是拥塞所引起,从而降低一半的速率,是对网络资源的极大浪费。

2)引起buffer膨胀

会在网络中设置一些buffer,用于吸收网络中的流量波动,在连接开始阶段,基于丢包的拥塞控制方法倾向于填满buffer。当瓶颈链路的buffer很大时,需很长时间才能将buffer中的数据包排空,造成很大网络延时,此情况称之为buffer膨胀。在一个FIFO队列管理方式的网络中,过大的buffer以及过长的等待队列,在某些情况下不仅不能提高系统的Tput甚至可能导致系统的吞吐量近乎于0。且当所有buffer都被填满时,会出现丢包。

基于时延

将时延增加视为出现拥塞,延时增加时增大cwnd,延时减小时减小cwnd,如Vegas、FastTCP等。

基于链路容量

实时测量网络带宽和时延,认为网络上报文总量>带宽时延乘积时出现了拥塞,如BBR。

基于学习

无特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习方法形成一个控制策略,如Remy。

经典拥塞控制算法

Reno/cubic

Cubic是Reno的高级版本,2者同属一种思想(将一种锯齿换成另一种锯齿而已)

它们是事件驱动,无论是丢包,还是RTT增加,都被视为一种严重事件,这些严重事件导致TCP拥塞控制系统在"To find current bandwidth","To avoid congestion"及

"To probe more bandwidth"之间切换,最终结果是促使拥塞算法(无论Reno,Vegas还是CUBIC)调整窗口大小。

谁来发现这些事件是否发生?TCP拥塞控制状态机。拥塞控制状态机直接主导这些状态的切换,只要它发现丢包,不管是否为真,都会拉低窗口值。

所以,Reno/CUBIC的窗口调整是被动的。

bbr

bbr是反馈驱动的,bbr内置了自主的调速机制,不受TCP拥塞控制状态机的控制,bbr算法是自闭的

bbr周期性的试图探测是否有更多的带宽,若有,那么就利用它,若无,就退回到之前的状态。

所以说,bbr的窗口调整是主动的。

完成最大带宽和最小RTT的测量

  1. Bbr根据测量的max带宽和min rtt来决定发包速率

拥塞控制算法的核心是选择一个有效策略来控制cwnd变化,有几种经典方法:

1、Vegas

将RTT增加作为网络拥塞的信号,RTT增加,cwnd减小,RTT减小,cwnd增加。即,Vegas通过比较实际Tput和期望Tput来调节cwnd,期望Tput:Expected=cwnd/BaseRTT,实际Tput:Actual=cwnd/RTT,diff=(Expected-Actual) * BaseRTT,BaseRTT-所有观测来回响应时间的最小值,一般是建立连接后所发的第一个数据包的RTT

Vegas定义了2个阈值a,b,当diff > b时,cwnd减小,当a <= diff <=b时,cwnd不变,当diff < a时,cwnd增加

Vegas算法采用RTT的改变来判断网络的可用带宽,能精确测量网络的可用带宽,效率高。但,网络中Vegas与其它算法共存时,基于丢包的拥塞控制算法会尝试填满网络中的buffer,导致Vegas计算的RTT增大,进而降低cwnd,使传输速度变慢,因此Vegas未能在Internet上普遍采用

适用场景:网络中只存在Vegas一种拥塞控制算法,竞争公平的情况

2、Reno

Reno将拥塞控制的过程分为4个阶段:慢启动、拥塞避免、快重传和快恢复,是现有的众多拥塞控制算法的基础

慢启动阶段,在未出现丢包时每收到一个ACK就将cwnd大小加1 MSS,每轮发送窗口增加一倍,呈指数增长,若出现丢包,则将cwnd减半,进入拥塞避免阶段;当窗口达到ssthresh或出现丢包时,进入拥塞避免阶段,窗口每轮次加1,呈线性增长;当收到对一个报文的3个DACK时,认为此报文的下一个报文丢失了,进入快重传阶段,立即重传丢失的报文,而非等待超时重传;快重传完成后进入快恢复阶段,将ssthresh修改为当前cwnd值的一半,同时cwnd=ssthresh,然后进入拥塞避免阶段,重复上诉过程

Reno算法将收到ACK这一信号作为cwnd增长的依据,在早期低带宽、低时延的网络中效能很好,但随着网络带宽和延时的增加,Reno缺点渐现,发送端从发送报文到收到ACK经历一个RTT,在高带宽延时(High Bandwidth-Delay Product,BDP)网络中,RTT很大,导致cwnd增长很慢,传输速度需经过很长时间才能达到最大带宽,导致带宽利用率将低

适用场景:适用于低延时、低带宽的网络

BIC

对窗口可能的最大值进行二分查找,它基于以下事实:

1)若发生丢包时,cwnd=W1,则要保持线路满载却不丢包,实际的窗口max应在W1以下;

2)若检测到丢包,且已将窗口乘性减到了W2,那么实际的窗口值应在W2以上 因此,在TCP快速恢复阶段过去后,便开始在W2~W1这个区间内进行二分搜索,寻找窗口的实际max。于是定义W1为Wmax,定义W2为Wmin,整个二分搜索的过程采用ACK驱动:每收到一个ACK时,便将窗口设置到Wmax和Wmin的中点,一直持续到接近Wmax。可见BIC的行为是ACK驱动,而ACK在何时到来与RTT相关。BIC算法已在没有丢包时无限接近了Wmax,代表带宽有空闲资源了,此次的最大带宽已不止Wmax了,而是>Wmax的一个值

1)怎么发现已找到最大带宽:往这之后就会发生丢包;

2)怎么找到最大带宽:若度过了Wmax都没有丢包,说明新的Wmax还未达到,此时BIC采取一种非常简单直接的方法:按照逼近Wmax的路径倒回去,即采用与之对称的方案

缺点:

2个RTT不同的连接,其通过BIC算法搜索到Wmax的时间不同,进而其进入Max-Probe阶段也不同,因此空闲带宽会被RTT短的那个连接无情占有。即公平性的问题:TCP对带宽的利用并不公平。这是一个,抢占带宽的算法

Cubic

Cubic是Linux内核2.6后的默认TCP拥塞控制算法,使用一个立方函数作为cwnd的增长函数,其中,C是调节因子,t是从上一次缩小cwnd经过的时间,Wmax是上一次发生拥塞时的窗口大小,β是乘法减小因子。从函数中可看出cwnd的增长不再与RTT有关,而仅取决上次发生拥塞时的最大窗口和距离上次发生拥塞的时间间隔值

Cubic cwnd增长曲线如下,凸曲线部分为稳定增长阶段,凹曲线部分为最大带宽探测阶段。如图2所示,在刚开始时,cwnd增长很快,在接近Wmax口时,增长速度变平缓,避免流量突增而导致丢包;在Wmax附近,cwnd不再增加;之后开始缓慢探测网络最大Tput,保证稳定性(在Wmax附近容易出现拥塞),在远离Wmax后,增大窗口增长的速度,保证了带宽的利用率

当出现丢包时,将cwnd进行乘法减小,再继续开始上述增长过程。此方式可使得cwnd一直维持在Wmax附近,从而保证了带宽的利用率

优点

只要未出现丢包,就不会主动降低自己的发送速度,可最大程度的利用网络剩余带宽,提高Tput,在高带宽、低丢包率的网络中可发挥较好的性能

缺点

Cubic同之前的拥塞控制算法一样,无法区分拥塞丢包和传输错误丢包,只要发现丢包,就会减小cwnd,降低发送速率,而事实上传输错误丢包时网络不一定发生了拥塞,但传输错误丢包的概率很低,所以对Cubic算法的性能影响不是很大

另一个不足之处是过于激进,在未出现丢包时会不停增加cwnd的大小,向网络注入流量,将网络设备的buffer填满,出现Bufferbloat(buffer膨胀)。由于buffer长期趋于饱和状态,新进入网络的的数据包会在buffer里排队,增加无谓的排队时延,buffer越大,时延越高。另外Cubic算法在高带宽利用率的同时依然在增加cwnd,间接增加了丢包率,造成网络抖动加剧

适用场景:适用于高带宽、低丢包率网络,能有效利用带宽

BBR

谷歌在2016年提出的一种新的拥塞控制算法,目前已集成到Linux 4.9以上版本的内核中

BBR算法不将出现丢包/时延增加作为拥塞的信号,而是认为当网络上的数据包总量>瓶颈链路带宽和时延的乘积时才出现了拥塞,所以BBR也称为基于拥塞的拥塞控制算法。BBR算法周期性探测网络的容量,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为cwnd(交替测量原因:极大带宽和极小时延不可能同时得到,带宽极大时网络被填满造成排队,时延必然极大,时延极小时需数据包不被排队直接转发,带宽必然极小),使得cwnd始的值始终与网络的容量保持一致

由于BBR的cwnd是精确测量出来的,不会无限的增加cwnd,也就不会将网络设备的buffer填满,避免了出现Bufferbloat,使得时延大大降低。如图4所示,网络buffer被填满时时延为250ms,Cubic算法会继续增加cwnd,使得时延持续增加到500ms并出现丢包,整个过程Cubic一直处于高时延状态,而BBR由于不会填满网络buffer,时延一直处于较低状态

由于BBR算法不将丢包作为拥塞信号,所以在丢包率较高的网络中,BBR依然有极高的Tput

BBR算法是反馈驱动的,有自主调节机制,不受TCP拥塞控制状态机的控制,通过测量网络容量来调整cwnd,发送速率由自己掌控,而传统的拥塞控制算法只负责计算cwnd,而不管发送速率,怎么发由TCP决定,这样会在瓶颈带宽附近因发送速率的激增导致数据包排队或出现丢包

BBR的四个状态(启动、排空、带宽探测、时延探测)

缺点

设备队列缓存较大时,BBR可能会竞争不过Cubic等较激进算法,原因是BBR不主动去占据队列缓存,若Cubic的流量长期占据队列缓存,会使得BBR在多个周期内测量的极小RTT增大,进而使BBR的带宽减小

(1)当连接建立时,BBR采用类似标准TCP的slow start,指数增加发送速率,目的也是尽可能快的占满管道,经过3次发现投递率不再增长,说明管道被填满,开始占用buffer它进入排空阶段(事实上此时占的是3倍带宽*延迟)

(2)在排空阶段,指数降低发送速率(相当于是startup的逆过程)将多占的2倍buffer慢慢排空

(3)完成上面2步,进入稳定状态后,BBR改变发送速率进行带宽探测:先在一个RTT时间内增加发送速率探测最大带宽,若RTT没有变化,后减小发送速率排空前一个RTT多发出来地包,后面6个周期使用更新后的估计带宽发包

(4)还有一个阶段是延迟探测阶段:BBR每过10s,如果估计延迟不变,就进入延迟探测阶段,为了探测最小延迟,BBR在这段时间内发送窗口固定为4个包,即几乎不发包,占整个过程2%的时间。

适用场景:适用于高带宽、高时延、有一定丢包率的长肥网络,可有效降低传输时延,并保证较高的Tput

Remy

也称为计算机生成的拥塞控制算法,采用机器学习的方式生成拥塞控制算法模型。通过输入各种参数模型(如瓶颈链路速率、时延、瓶颈链路上的发送者数量等),使用一个目标函数定量判断算法的优劣程度,在生成算法过程中,针对不同的网络状态采用不同方式调整cwnd,反复修改调节方式,直到目标函数最优,最终会生成一个网络状态到调节方式的映射表,在真实的网络中,根据特定的网络环境从映射表直接选取cwnd

Remy试图屏蔽底层网络环境的差异,采用一个通用的拥塞控制算法模型来处理不同的网络环境。这种方式比较依赖输入的训练集(历史网络模型),若训练集能全面覆盖所有可能出现的网络环境及拥塞调节算法,Remy算法在应用到真实的网络环境中时能表现很好,但若真实网络与训练网络差异较大,Remy算法的性能会较差

适用场景:网络环境为复杂的异构网络,希望计算机能针对不同网络场景自动选择合适的拥塞控制方式,要求现有的网络模型能覆盖所有可能出现情况

拥塞控制状态机

和TCP一样,拥塞控制算法也有状态机。当发送方收到一个Ack时,Linux TCP通过状态机(state)来决定接下来的行为,是应降低cwnd大小,或保持cwnd,或增加cwnd。若处理不当,可能会导致丢包或超时。

拥塞控制状态机的状态有5种:Open,Disorder,CWR,Recovery和Loss

Open状态

是拥塞控制状态机的默认状态。

此状态下,当ACK到达时,发送方根据cwnd是小于还是>ssthreshssthresh,来按照慢启动或拥塞避免算法来调整cwnd。

Disorder状态

当发送方检测到DACK或SACK时,状态机将变为Disorder状态。在此状态下,发送方遵循飞行(in-flight)包守恒原则,即一个新包只有在一个老包离开网络后才发送,即发送方收到老包的ACK后,才会再发送一个新包。

CWR状态

发送方收到一个拥塞通知时,并不会立刻减小cwnd,而是每收到2个ACK(每隔一个新的ACK到达)就减少一个段,直到窗口的大小减半为止。当cwnd正在减小且网络中有无重传包时,该状态就叫CWR(Congestion Window Reduced)状态。CWR状态可转变成Recovery或Loss状态。

Recovery状态

当发送方收到足够(推荐为3个)的DACK后,进入该状态。在该状态下,cwnd每收到2个ACK就减少一个段(segment),直到cwnd=ssthresh,即刚进入Recover状态时cwnd的一半大小。 发送方保持 Recovery 状态直到所有进入 Recovery状态时正在发送的数据段都成功被确认,然后发送方恢复成Open状态,重传超时有可能中断 Recovery 状态,进入Loss状态。

Loss状态

当一个RTO到期后,发送方进入Loss状态。所有正在发送的数据标记为丢失,cwnd设置为一个段(segment),发送方再次以慢启动算法增大cwnd。

Loss 和 Recovery 状态的区别:Loss状态下,cwnd在发送方设置为一个段后增大,而 Recovery 状态下,cwnd只能被减小。Loss 状态不能被其他状态中断,因此,发送方只有在所有 Loss 开始时正在传输的数据都得到成功确认后,才能退到 Open 状态。

TCP 拥塞控制的目标是最大化利用网络上瓶颈链路的带宽。

水管的容积=水管粗细 × 水管长度

网络内尚未被确认收到的数据包数量=网络链路上能容纳的数据包数量=链路带宽 × 往返延迟

现有tcp拥塞控制算法:Reno、HSTCP、BIC、Vegas、Westwood

一、TCP拥塞控制的研究框架

目前有非常多的 TCP 的拥塞控制协议,例如:

基于丢包的拥塞控制:将丢包视为出现拥塞,采取缓慢探测的方式,逐渐增大cwnd,当出现丢包时,将cwnd减小,如 Reno、bic、Cubic 等。

基于时延的拥塞控制:将RTT增加视为出现拥塞,延时增加时增大cwnd,延时减小时减小cwnd,如 Vegas、FastTCP 等

基于链路容量的拥塞控制:实时测量网络带宽和时延,认为网络上报文总量>带宽时延乘积时出现了拥塞,如 BBR。

基于学习的拥塞控制:没有特定的拥塞信号,而是借助评价函数,基于训练数据,使用机器学习的方法形成一个控制策略,如 Remy。

Reno

它将拥塞控制的过程分为4个阶段:慢启动、拥塞避免、快重传和快恢复

慢启动阶段思路是不要一开始就发送大量的数据,先探测一下网络的拥塞程度,即说由小到大逐渐增加cwnd的大小,在未出现丢包时每收到一个 ACK 就将cwnd大小加一(单位是 MSS,最大单个报文段长度),每轮次发送窗口增加一倍,呈指数增长,若出现丢包,则将cwnd减半,进入拥塞避免阶段;

当窗口达到ssthresh或出现丢包时,进入拥塞避免阶段,窗口每轮次加一,呈线性增长;当收到对一个报文的3个重复的 ACK 时,认为这个报文的下一个报文丢失了,进入快重传阶段,要求接收方在收到一个失序的报文段后就立即发出DACK(为的是使发送方及早知道有报文段没有到达对方,可提高网络Tput约20%)而不要等到自己发送数据时捎带确认;

快重传完成后进入快恢复阶段,将ssthresh修改为当前cwnd值的一半,同时cwnd值等于ssthresh,然后进入拥塞避免阶段,重复上述过程。

BBR

BBR 是谷歌在 2016 年提出的一种新的拥塞控制算法,已在 Youtube 服务器和谷歌跨数据中心广域网上部署,据 Youtube 官方数据称,部署 BBR 后,在全球范围内访问 Youtube 的延迟降低了 53%,在时延较高的发展中国家,延迟降低了 80%。

BBR 算法不将出现丢包或时延增加作为拥塞的信号,而是认为当网络上的数据包总量>瓶颈链路带宽和时延的乘积时才出现了拥塞,所以 BBR 也称为基于拥塞的拥塞控制算法(Congestion-Based Congestion Control),其适用网络为高带宽、高时延、有一定丢包率的长肥网络,可有效降低传输时延,并保证较高的Tput,与其他2个常见算法发包速率对比如下:

BBR 算法周期性地探测网络的容量,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为cwnd大小,使得cwnd始的值始终与网络的容量保持一致。

在有一定丢包率的网络链路上充分利用带宽——适合高延迟、高带宽的网络链路。

降低网络链路上的 buffer 占用率,从而降低延迟——适合慢速接入网络的用户