通过本文,您将了解以下内容:拥塞控制的概念及其背景流量控制与拥塞控制的区别以及联系拥塞的主要过程详解控友仔细研究,让offer来。再暴力一点!0x01。TCP/IP协议栈简述我们来看看维基百科对TCP/IP的介绍。作者做了少量修改以保证表述的流畅:InternetProtocolSuite是一种网络通信模型,是网络传输协议的整个家族。协议簇包括两个核心协议:TCP(TransmissionControlProtocol)和IP(InternetProtocol),因此常被称为TCP/IP协议族。TCP/IP协议对基本流程进行了标准化,例如在目的地应如何封装、寻址、传输、路由和接收数据。它将通信过程抽象为四个层次,采用协议栈的方法实现不同的通信协议。实际的四层结构是七层OSI模型的简化。我们可以看到,TCP/IP协议栈是一个简化的分层模型,是连接互联网世界万物的基石。先看一张七层模型对比四层模型的简化图:TCP/IP协议栈太大,篇幅有限,不再做更详细的描述。0x02。流量控制和拥塞控制TCP是一种面向连接的、可靠的、全双工的传输协议。前人写了很多复杂的算法来保护它,像海尔兄弟这样的一组算法:流量控制和拥塞控制,这就是我们今天的主角。2.1流量控制简介流量控制和拥塞控制在中文中无法从字面上区分。本质上,这对算法既不同又相关。维基百科对流量控制的描述FlowControl:在数据通信中,流量控制是管理两个节点之间数据传输速率的过程,以防止快速的发送方压倒慢速的接收方。它为接收方提供了一种控制传输速度的机制,这样接收节点就不会被来自发送节点的数据淹没。翻译:在数据通信中,流量控制是管理两个节点之间数据传输速率的过程,以防止快速发送方压倒慢速接收方。它为接收器提供了一种控制传输速度的机制,以便接收节点不会被来自发送节点的数据淹没。可见,流量控制是一种约定通信双方数据量的机制,具体是借助于TCP协议和窗口协议的确认ACK机制来完成的。窗口分为固定窗口和可变窗口。可变窗口也是一个滑动窗口。简单的说就是通信的双方根据接收方的接收情况,动态地告诉发送方可以发送的数据量,从而实现发送方和接收方之间的数据收发。匹配能力。这个过程很容易捕捉。可以在电脑上使用wireshark抓包,也可以在服务器上使用tcpdump抓包。大白在自己的电脑上用wireshark抓了一个:下面通过两台主机的交互来简单了解一下流控过程:接收方回复消息头解释:图中的RcvBuffer是接收区的总大小,缓冲的数据是当前占用数据,freebufferspace为当前剩余空间,rwnd为freebufferspace区的字节数。HostB将当前的rwnd值放入报文头的receivewindow字段中,通知HostA自己还有多少可用空间,而HostA将未确认数据量控制在rwnd值范围内,避免HostB的接收缓冲区溢出。可见流控是一种端到端的微观数据策略。双方在数据通信过程中不关心链路带宽,只关心通信双方接收和发送缓冲区的空间大小。可以说是一种速率流量匹配策略。流量控制就像现实生活中物流领域的两个仓库A和B。A向B运送货物时,只关心B仓库的剩余空间来调整自己的出货量,并不关心高速公路是否拥堵。2.2拥塞控制的必要性我们在微观层面提到了点到点的流量控制,但不禁要思考一个问题:流量控制够了吗?答案是否定的。我们还需要一个宏观层面的控制来避免网络链路拥塞。否则,即使是最好的端到端流控算法也会面临丢包、乱序、重传等问题,只能造成恶性循环。我们从更高的角度来看大量TCP连接复用网络链路的通信过程:所以拥塞控制与每一个端到端的连接都有很大的关系。这就是流量控制和拥塞控制之间的深层联系。所谓每一个连接都顺畅,那么整个复杂的网络链路在很大程度上也是顺畅的。在进行拥塞控制之前,我们先考虑几个问题:如何感知拥塞。TCP连接的发送端在向对端发送数据时,需要根据当前的网络状况调整发送速率,因此感知能力至关重要。一个TCP连接的发送方一般根据丢包来判断当前网络是否拥塞,丢包可以通过重传超时RTO和重复确认来判断。如何使用带宽确实对拥塞有很大的影响,但是为了低带宽利用率而低速发送数据包也是非常不明智的。因此,要充分利用带宽,发送数据既不能太低也不能太高,要保持动态稳定的速率。提高带宽利用率相对困难,就像在黑夜中避开障碍物一样。发生拥塞时如何调整当发生拥塞时,我们需要一套对策来防止拥塞进一步恶化,恢复连接流量,这也是拥塞控制算法的精髓所在。0x03。理解拥塞控制前面我们提到了拥塞控制的必要性和重要问题。接下来,我们就来看看前辈们是如何设计并实现精彩的拥塞控制策略的吧!3.1拥塞窗口cwnd从流控中我们可以知道接收方已经在header中给出了rwnd的接收窗口大小,发送方无法根据接收方的rwnd限制自行发送数据,因为网络链路是多路复用的,需要考虑当前链路情况来判断数据量。这是我们要提到的另一个变量cwnd。作者找到了一篇关于rwnd和cwnd的英文解释:CongestionWindow(cwnd)isaTCPstatevariable,它限制了TCP在收到ACK之前可以发送到网络中的数据量。ReceiverWindow(rwnd)是一个变量,用于通告目标端可以接收的数据量。这两个变量一起用于调节TCP连接中的数据流、最小化拥塞并提高网络性能。笔者在rfc5681文档中也看到了cwnd的定义:这个解释指出cwnd是由sender维护的,cwnd和rwnd并不冲突。发送方需要结合rwnd和cwnd这两个变量来发送数据。如图:cwnd的大小与MSS的最大数据段有直接关系,MSS是TCP段中数据域的最大长度,即MSS=TCP段长度-TCP头长度。3.2基本拥塞控制策略拥塞控制是一个动态过程。它不仅需要提高带宽利用率,发送尽可能多的数据,还要避免网络拥塞、丢包、RTT增加等问题。基于这种高要求,任何单一的策略都无法应对,所以TCP的拥塞控制策略实际上是一个阶段和策略的综合过程:注:有些版本的TCP算法不一定没有快恢复阶段。重传RTO的过程:3.3TCP算法的常见版本事实上,TCP算法有很多版本,每个版本都有一些差异。这里简单介绍一下维基百科:算法命名规则TCP+算法名的命名方式最早出现在KevinInFall和SallyFloyd1996年的论文中。TCPTahoe和TCPReno这两种算法的代号分别取自LakeTahoe和Reno。两种算法大致相同。对于丢包事件,根据重传超时和重复确认进行判断。但是,对于重复确认的处理,两者是不同的。对于超时重传RTO的情况,两种算法都将拥塞窗口减小到1MSS,然后进入慢启动阶段。TCPTahoe算法:如果收到3次重复确认,即第四次收到相同确认号的报文段确认,且该报文段对应的是空载报文段,接收窗口没有变化,则Tahoe算法将进入快速重传和慢速启动阈值改为当前拥塞窗口的一半,拥塞窗口减小为1MSS,重新进入慢速启动阶段。TCPReno算法:如果收到三个重复的确认,Reno算法进入快速重传,只将拥塞窗口减半以跳过慢启动阶段,将慢启动阈值设置为当前新的拥塞窗口值,进入快速恢复过程新的设计阶段。TCPNewRenoTCPNewReno是一种改进算法,用于改进TCPReno快速恢复阶段的重传。NewReno的运行效率在错误率低的时候和SACK相当,在错误率高的时候还是比Reno好。TCPBIC和TCPCUBICTCPBIC旨在优化高速和高延迟网络的拥塞控制。他们的拥塞窗口算法采用二分查找算法,试图找到可以长期维持的拥塞窗口的最大值。Linux内核在2.6.8到2.6.18中使用这个算法作为默认的TCP拥塞算法。CUBIC是比BIC更温和、更系统的分支版本。它使用三次函数代替二进制算法作为其拥塞窗口算法,并使用函数拐点作为拥塞窗口的设置值。Linux内核在2.6.19TCP拥塞算法后默认使用该算法。TCPPRRTCPPRR旨在提高恢复期间发送数据的准确性。该算法确保恢复后的拥塞窗口大小尽可能接近慢启动阈值。在谷歌进行的测试中,平均延迟可以减少3~10%,恢复超时可以减少5%。PRR算法作为Linux内核3.2版本的默认拥塞算法后。TCPBBRTCPBBR是谷歌设计并于2016年发布的拥塞算法,该算法认为随着网络接口控制器逐渐进入千兆速度,丢包不应被视为拥塞的主要决定因素,因此基于模型的拥塞控制算法可以有更高的吞吐量和更低的延迟,BBR可以用来替代其他流行的拥塞算法。谷歌将该算法应用在YouTube上,使全球平均YouTube网络吞吐量提高了4%,BBR后来被移植到Linux内核4.9版本中。3.4拥塞控制过程详解我们用典型的慢启动、拥塞避免、快速重传、快速恢复四个过程来说明。慢启动慢启动是指对于一个新启动的网络连接,发送速度不是一步到位而是试探性的提高。具体为:在连接初始建立时,发送端初始化拥塞窗口cwnd为m,然后发送端在一个RTT内每次接收。对于一个ACK包,cwnd线性增加1,发送方每经过一个RTT,cwnd=cwnd*2呈指数增长,直到一段时间后cwnd达到慢启动阈值ssthresh。之后cwnd不再呈指数增长,进入拥塞避免阶段(注意cwnd增长的单位是MSS)。当然,如果慢启动阶段还没有达到阈值ssthresh,出现丢包就进入快重传阶段,需要注意的是,如果网络状况良好,RTT时间很短,那么慢启动阶段很快就会达到一个比较高的发送速率,所以把slowstart理解为试探性的开始比较形象。拥塞避免当cwnd的值在慢启动阶段达到ssthresh后,就不再疯狂增加,进入更理性的线性阶段,直到发送丢包。这次的阈值ssthresh是上次丢包时cwnd的1/2,所以这是一个连续的过程。本次丢包时,ssthresh的值仍然会被调整。具体的拥塞避免增长过程:发送方每收到一个ACK包就会设置cwnd=cwnd+1/cwnd,每经过一个RTTcwnd就会加1。超时重传和快速重传TCP作为一个可靠的协议面临的一个大问题就是丢包,这就需要重传。因此,发送方需要根据接收方回复的ACK确认包是否丢失,发送方在发送数据后启动定时器,如图:RTO随着复杂的网络环境动态变化,而拥塞控制中的重传超时会大大减少cwnd。如果网络状况不是那么糟糕,偶尔网络抖动导致的丢包或者拥塞也很常见,那么触发的慢启动会降低通信性能,于是出现了快速重传机制。与超时重传相比,所谓快重传会减少重传等待时间,尽量避免慢启动,保证最小的性能损失,如图:快重传和超时重传的区别取决于cwnd当拥塞发生时。超时重传会将cwnd修改为原来的值,也就是慢启动的值。快速重传会将cwnd减半,两者都会将ssthresh设置为cwnd的一半。从两者的区别可以看出,快速重传更加主动,有助于保证链路的传输性能。但也有研究表明,3个ACK的机制也存在问题。本文不做深入阐述。感兴趣的读者可以自行查阅。快速重传是基于网络条件还不错的假设。所以,当实际网络真的很好的时候,快速重传还是很有用的。在较差的网络环境下,很多算法都难以保证效率。快速恢复在快速重传后会进入快速恢复阶段。此时cwnd为上次拥塞时cwnd的1/2,之后cwnd会线性增加,重复前面的过程。
