当前位置: 首页 > Linux

TCP拥塞控制的基础

时间:2023-04-07 01:52:11 Linux

TCP有四个要点,一是连接,二是可靠传输,三是数据按到达,四是端到端的流量控制。注意TCP在设计的时候只是保证了这四点。这时候虽然有些问题,但是很简单。但是,更大的问题很快就会出现,需要考虑IP网络相关的问题,比如公平性。效率,所以加了拥塞控制,让TCP变成现在这个样子。为什么要进行拥塞控制要回答这个问题,首先要知道TCP什么时候发生拥塞。TCP作为端到端的传输层协议,不关心双方在物理链路上会经过多少个路由器和交换机,也不关心报文传输的路径和下一个路径。这是IP层应该考虑的。然而在实际网络应用中,TCP连接的两端可能远隔千山万水,数据包也需要经过多个路由器和交换机的转发。开关设备的性能不是无限的!当多个入接口的报文从同一个出接口转发时,如果出接口的转发速率达到限制,报文将开始在交换设备入接口的缓冲队列中堆积。但是,队列的长度也是有限的。当队列满时,后续的输入包只能被丢弃。对于TCP的发送端,看到的是发送超时丢失。网络资源由每个连接共享,使每个人都可以完成数据传输。因此,TCP在感知到传输拥塞时需要降低发送速率,等待拥塞解决。如何进行拥塞控制Congestionwindowcwnd首先需要明确的是,TCP是在发送端进行拥塞控制的。TCP为每个连接准备了一个变量cwnd1来记录拥塞窗口的大小,它限制了本地TCP可以向network2发送的最大数据包数。显然,该值越大,连接的吞吐量越高,但也更容易造成网络拥塞。所以TCP拥塞控制的本质就是根据丢包情况调整cwnd,让传输吞吐率尽可能的大!而且不同的拥塞控制算法对cwnd的调整方式不同!注1:本文中的cwnd是根据发送方的最大报文段长度SMSS来的。注2:这个数字也受对端通告的窗口大小的限制。Linux用户可以使用ss--tcp--info查看链路拥塞控制算法的cwnd值TCP从诞生之日起就有多种拥塞控制算法,到现在还在不断提出新的!其中,TCPTahoe(1988)和TCPReno(1990)是最早出现的两种算法。虽然看起来很古老,但Reno算法直到现在仍然被广泛使用。Tahoe在Tahoe的基础上提出了1)慢启动,2)拥塞避免,3)快速重传Reno增加了4)快速恢复。Tahoe算法的基本思想是在没有丢包的情况下,先设置一个合理的初始窗口值,慢慢增大窗口大小,逐渐逼近吞吐量的上界。当出现丢包时,迅速缩小窗口大小,等待拥塞消除。Tahoe拥塞避免(congestion-avoidance)当我们理解拥塞控制算法时,我们可以假设发送方一次性发送出整个拥塞窗口大小的消息,然后等待响应。Tahoe使用AdditiveIncrease,MultiplicativeDecrease(AIMD)的方法来完成拥塞窗口的缓慢增加和快速减少:发送方发送整个窗口的数据:如果没有丢包,则cwnd=cwnd+1如果有a丢包,那么cwnd=cwnd/2为什么丢包后除以2呢?这里的2其实是一个折衷值!用下面的例子来解释吧!物理传输路径会有延迟。这种延迟也让传输链路有了传输容量(transitcapacity)的概念。同时,我们称交换设备的队列容量为队列容量(queuecapacity)。例如下面的连接,传输容量为M,队列容量为N。当cwnd小于M时,R的队列将不会被使用,此时不会发生拥塞;当cwnd继续增加的时候,R的队列就会开始被使用,其实已经是阻塞了!但是A是感知不到的,因为没有丢包!当cwnd继续增加到M+N时,如果cwnd再次增加,就会出现丢包。这时候拥塞控制算法需要减少cwnd,那么应该减少多少呢?当然,减少到不使用R的缓存,或者使cwnd=M,可以快速解除阻塞。这是最理想的情况!实际情况是A并不知道M和N有多大(或者是什么关系),它只知道当cwnd超过M+N时,就会出现丢包!所以我们妥协,假设M=N,那么当cwnd=2N是丢包的临界点,为了解封,让cwnd=cwnd/2=N可以解封3!所以cwnd的变化趋势如上,图中上方的红色曲线表示丢包。这种稳定状态也称为拥塞避免阶段(congestion-avoidancephase)。在实际的算法实现中,拥塞避免阶段的cwnd在每次收到ACK时更新:cwnd+=1/cwnd。如果你仔细计算一下,你会发现这比更新整个窗口的cwnd+=1略显不足!注3:后面会提到,此时Tahoe会将cwnd设置为1,然后快速恢复到cwnd/2Tahoe慢启动(SlowStart)Tahoe需要选择一个cwnd的初始值,但是发送方并不知道cwnd是多少是合适的。所以我们只能从1开始加到4,如果这个时候开始加法增加,那就太慢了。例如,假设一个连接将发送5050条MSS大小的消息,根据加法增加,将需要100RTT才能完成传输(1+2+3+...+100=5050)。因此,Tahoe和Reno使用称为慢启动的算法快速增加cwnd。即只要不丢包,每次发送一整窗口的数据,cwnd=2Xcwnd。也就是说,在慢启动阶段(slow-startphase),当发送方收到ACK,令cwnd=cwnd+1注4RFC2581已经允许cwnd的初始值最大为2,RFC3390已经允许cwndcwnd的初始值最大为4,RFC6928已经允许cwnd的初始值最大为10。那么,慢启动阶段什么时候停止呢?或者什么时候进入前面的拥塞避免阶段?Tahoe算法定义了一个慢启动阈值(slow-startthreshold)变量。当cwndssthresh后,TCP进入拥塞避免阶段。ssthreshold的初始值是一个非常大的值。连接建立后,cwnd呈指数增长,直到丢包,慢启动阈值会被设置为cwnd/2。同时cwnd被设置为1,重新启动慢启动过程。这个过程如下图所示,可以看到,慢启动一点都不慢。TahoeFastRetransmit(快速重传)实际网络环境的拓扑结构可能非常复杂。由于等价路由等因素,即使是同一个TCP连接的报文也可能被路由器转发到不同的路径。因此,在接收端可能会出现乱序到达的数据包,甚至丢包!比如发送方发送数据DATA[1],DATA[2],...,DATA[8],但是由于某些因素,DATA[2]在传输过程中丢失了,接收方只收到了另外7个消息,它会连续回复多个ACK[1](要求发送方发送DATA[2])。这个时候sender是否还需要等待DATA[2]的回复超时(2个RTT)?快速重传的策略是,不要等待!当发送方收到第三次重复的ACK[1](即第四次ACK[1]),会立即重传DATA[2],然后进入慢启动阶段,设置ssthresh=cwnd/2,cwnd=1.如上图所示,cwnd的初始值为8,当发送方收到第三次重复的ACK[1]时,很快进入慢启动阶段,然后当再次收到ACK[1]时,由于cwnd=1且只有1,不会发送新的数据包。RenoFastRecovery(快速恢复)在快速重传中,当数据包乱序丢失时,拥塞窗口cwnd变为1。由于这个限制,在丢失的数据包被确认之前没有办法发送新的数据包。这大大降低了网络的吞吐量。为了解决这个问题,TCPReno在TCPTahoe的基础上增加了FastRecovery。fastrecovery的策略是在收到第三次重复ACK后快速重传丢失的数据包,然后设置sshthresh=cwnd/2设置cwnd=cwnd/2或者以上面的例子为例和fastretransmission是不同的,发送方收到第三次重复ACK后,cwnd变为5,EFS设置为7其中EFS表示发送方认为正在发送给对端的数据包(EstimatedFlightSize),或者是链路上的(inflight)包.一般来说,EFS等于cwnd。但说到快速恢复,就不一样了。假设在拥塞避免阶段cwnd=EFS=N,当开始快速恢复时,收到3个重复的ACK。注意这3个ACK不会占用网络资源(因为已经被对端接收到),所以EFS=N-3,而且由于启动了快恢复,肯定还有一个包没有到达,所以EFS=N-4,然后,本端会快速重传一条消息,EFS=N-3,这是上面EFS设置为7的源码,其他的就没什么好说的了。当EFS