拥塞控制
拥塞窗口 cwnd是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化。
发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。
由来
前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,即是端的流量控制
但是并能避免网络的中发生拥塞。网络出现拥塞时不加以控制就会导致路径中的某一个节点一直出现丢包,
目前解决办法就是发送方维护一个虚拟的拥塞窗口。
控制的目的就是避免「发送方」的数据填满整个网络
如何实现拥塞控制
- 慢启动:TCP初始阶段每收到一个ACK,将窗口大小加倍(指数性的增长),快速逼近网络的带宽容量。
- 拥塞避免:一旦发送窗口达到一定阈值(拥塞窗口的一半),TCP进入拥塞避免阶段。这个值为慢启动阈值
- 拥塞发生:TCP主要通过丢包和RTT检测拥塞的发生,TCP推断网络出现了拥塞,会试图采取措施来降低网络负载。这些是靠具体使用的TCP拥塞算法来进行测量判断。
- 超时重传:慢启动阈值降为超时前拥塞窗口的一半大小、拥塞窗口会降为1个MSS,并且重新回到慢启动阶段。
- 快速重传:当发送方连续收到三个重复的确认后,即认为有一个数据段丢失,并立即进行快速重传。
- 快速恢复:TCP Reno实现的,快速恢复状态下,发送方将拥塞窗口减半,并使用拥塞窗口的一半作为新的慢启动门限
拥塞算法
TCP Reno
TCP Reno是早期常见的TCP拥塞控制算法之一,
TCP Reno算法实现了一个名为“快速恢复”的机制,
如果收到三次重复确认,Reno算法则进入快速重传,只将拥塞窗口减半来跳过慢启动阶段,将慢启动阈值设为当前新的拥塞窗口值,进入一个称为“快速恢复”的新设计阶段。
对于RTO,是将拥塞窗口降为1个MSS,然后进入慢启动阶段。
TCP Cubic
TCP Cubic是一种改进的TCP拥塞控制算法,目前广泛使用,
优化高速高延迟网络也称长肥网络(LFN)。它使用三次函数代替二分算法作为其拥塞窗口算法,并且使用函数拐点作为拥塞窗口的设置值。
CUBIC 算法函数:cwnd = C(T− K) ^3 + Wmax
- w max:最后一次过载事件时的窗口大小
- T:自上次过载事件以来经过的时间
- C : 缩放常数
- K:上述函数在没有进一步丢包的情况下将W增加到Wmax经历的时间
该图显示了 CUBIC 过载窗口的典型过程
- 由于三次函数,CUBIC 在第 1 阶段很快接近这一点。
- 在第二阶段,CUBIC 会尝试尽可能长时间地接近该最佳点,以便尽可能最佳地使用带宽。
- 如果没有发生过载事件,CUBIC 会在第三阶段切换到指数增长阶段,以找到网络中新的最佳带宽。
TCP BBR
TCP BBR(Bottleneck Bandwidth and RTT)是一种新的TCP拥塞控制算法
以往大部分拥塞算法是基于丢包来作为降低传输速率的信号,而BBR则基于模型主动探测。该算法使用网络最近出站数据分组当时的最大带宽和往返时间来创建网络的显式模型。
Google在youtube上应用该算法,将全球平均的youtube网络吞吐量提高了4%,在一些国家超过了14%。
BBR之后移植入Linux内核4.9版本,并且对于QUIC可用。
BBR在动态环境中表现不佳,比如蜂窝网络。BBR存在不公平问题。
算法比较
可以通过cat /proc/sys/net/ipv4/tcp_congestion_control 来看拥塞算法,目前大部分为“TCP Cubic”
可以通过cat /proc/sys/net/ipv4/tcp_available_congestion_control查看支持的拥塞算法
拥塞控制的过程
- 链接建立初期,拥塞窗口一般为最大分片大小(Maximum segment size,MSS)的两倍
早起RFC建议是1,2,4个MSS,
目前是10个MSS的大小,一般为14600字节
- 如果发出去的包都得到确认,则增大拥塞窗口,RFC建议是每收到N个确认,就把拥塞窗口增加N个MSS,这个过程即称为慢启动
- 当收到 2 个的 ACK 确认应答后, cwnd 增加 2,2+2=4于是这次能发4个
- 当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4+4=8这次就能发8个。
- 慢启动持续一段时间后,拥塞窗口达到一个较大值,即慢启动门限 ssthresh (slow start threshold)状态变量。开始放缓增加窗口,RFC建议是每个往返时间增加一个MSS
- 比如发了8个MSS都被确认,拥塞窗口增加为:8+1=9
- 一直增长着后,网络就会慢慢进入了拥塞的状况了,拥塞会发生什么?
发出的数据包得不到确认,可能是延时也可能是丢包,因此等待一段(RTO)时间开始重传数据包,这种重传我们称为超时重传
- 当触发了重传机制(超时重传),也就进入了「拥塞发生算法」
超时重传后即需要调整拥塞窗口,
RFC建议把窗口降到1个MSS,再次进入慢启动
这个时候,sshresh 的值就有依据了,便会发生变化:
- Stevens认为ssthresh 设为 cwnd/2,
- 超时重传带来的影响
RTO阶段不能传入数据,相当于浪费了一段时间,RTO时间内就会把发送窗口内的数据发送完成,无法收到确认导致LastByteAcked指针会停滞,所以无法继续向外发送
拥塞窗口急剧缩小,接下来的传输就会慢的多
- 为了避免超时重传带来的影响,出现了快速重传
当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
为什么要满三次,因为网络包会有乱序,乱序包同样会触发重复的ACK
- 快速重传就说明网络没有严重拥塞,所以对应的也就对应的使用快速恢复算法,
- 拥塞窗口 cwnd = ssthresh + 3 ( 3 是确认有 3 个数据包被收到了)
- 重传丢失的数据包如果再收到重复的 ACK,那么 cwnd 增加 1
- 如果收到新数据的 ACK 后,设置 cwnd 为 ssthresh,接着就进入了拥塞避免算法
知识补充
理论上只要时间增加我们一定会碰到拥塞点为什么实际感觉不到?
- 很多场景交互式的数据量比较小
- 传输数据的时候采用同步方式,可能需要的窗口就比较小,
- 偶尔发生,持续的时间也不足以能感受的出来。