• TCP--拥塞控制


    大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家来访。

    TCP中另一个重要的点就是拥塞控制,TCP是无私的当它感受到网络拥堵了,就会降低自己的发送速率。

    前言

    有人说?我们有流量控制了,为什么还要有拥塞控制。因为流量控制只保证了数据不会超过接收窗口的缓存大小,但是TCP是感受不到网络的拥堵状态的。当网络拥堵了(比如接收端迟迟收不到ACK),发送端就会一直重传,导致网络更加拥堵,然后就是个恶性循环了。


    拥塞控制有4个经典的算法。

    • 慢启动
    • 拥塞避免
    • 快速重传
    • 快速恢复

    一、慢启动

    首先就是慢启动。
    在慢启动的状态,cwnd(拥塞窗口)会被初始化为1个MSS(MSS介绍见下图)。
    MTU和MSS.webp
    发送端每次接收到一个ACK,cwnd就会增加一个MSS,并且发送两个最大长度的报文段,然后这两个报文段收到确认,cwnd就又会增加两个MSS。这样就相当于每经过1个RTT(不考虑发送时延),cwnd就会翻倍,以2的指数级增长。
    TCP慢重传.webp


    那什么时候结束指数增长呢?
    发送端会维护一个ssthresh(慢启动阈值),为了放置cwnd增长过大引起网络拥塞。

    • 发生了阻塞(丢包了等等)

    发送端会将ssthresh设置为当前cwnd值的一半,然后将cwnd设置为1,重新开始慢启动。
    (ssthresh = cwnd / 2,cwnd = 1)。

    • cwnd==ssthresh

    发送端会结束慢启动进入拥塞避免模式(下文介绍)。

    • 检测到3个冗余ACK

    TCP会执行快速重传,然后进入快速恢复状态。


    别看它这名字里面有个慢字,其实它是指数上升的。

    二、拥塞避免

    拥塞避免采取了一种保守的方式来提升cwnd,每个RTT才将cwnd的值加1,也就是1、2、3这样的线性增长。


    当网络出现拥塞的时候
    发送端会将ssthresh设置为当前cwnd值的一半,然后将cwnd设置为1。
    这样就是为了减少数据的发送量,让路由器缓存中的数据先发送发送。
    现在看来无论是慢启动状态还是拥塞避免状态,只要网络拥塞了,发送端会将ssthresh设置为当前cwnd值的一半,然后将cwnd设置为1。
    下图是慢启动和拥塞避免算法的一个曲线图。
    慢启动和拥塞避免.webp




    但是这样设置(暴力的将ssthresh=cwnd/2)有点弊端,就是比如说两个相同的拥塞窗口中,丢包的比例不一样,如果都将ssthresh设置为当前cwnd值的一半太过暴力。比如说下面这种情况。
    RFC弊端.webp
    像上图这种情况两个拥塞窗口都是16MSS,但是丢包的比例不一样。按正常思维来说应该第一个丢包比例少的设置的ssthresh应该大点。但是如果按照上面那种两个都是cwnd/2,就有点问题。
    由此Westwood算法产生了,当丢包很轻微时,由于Westwood能估算出当前拥塞并不严重,所以不会大幅度减少临界窗口值,传输速度得以保持。更多的关于Westwood算法的这里没有写,有可能以后我会补充上。\

    三、快速重传

    快速重传是基于数据的一种重传方式。
    快速重传.webp
    服务端每次收到数据后,就会返回一个ACK(确认序列号)表示下次希望收到的数据的序号,并且表示该序号之前的数据都已经成功接受。

    TCP默认是累计确认机制,当中间有包乱序到达了,TCP只会反复确认最后一个按序到达的数据,TCP只能知道下一个数据没有按时达到,之后的数据怎么样了并不知道。

    如图所示,第二个数据丢失了,服务端一直返回ACK2(因为一直没有收到第二个数据),当客户端收到3次同样的ACK后,客户端就会重传丢失的那个数据。然后服务端传回一个ACK6,表示6之前的数据都已经成功接受了。客户端收到3个相同的ACK后,就在超时重传时间之前进行重传。


    但是这样还是有个问题,就是重传的时候,是重传一个数据,还是重传所有数据呢。
    比如说数据2、3都丢失了,因为ACK只能表示下一次应该重传的一个数据,不能表示多个数据,所以即便数据3也丢失了,它依然是返回ACK2。
    如果重传2号数据,那3号数据也丢失了。如果都重传一次,显然是一次不必要的资源浪费。
    这就得看SACK了,关于SACK具体详看我的另一篇博客

    四、快速恢复

    当接收方收到3个相同的ACK的时候,发送端会认为网络已经拥塞,会进入快速恢复状态,发送端会将ssthresh设置为当前cwnd的一半,然后cwnd设置为一半然后再加三。(ssthresh = cwnd / 2,cwnd = cwnd / 2 + 3)。
    但是之后不进行慢启动,之后执行拥塞避免算法。

    慢恢复.webp

    TCP Tahoe 和 TCP Reno

    TCP也是在不断迭代的,最早的TCP版本叫做TCP Tahoe,目前使用最广泛的版本是TCP Tahoe。

    ssthresh设置为当前cwnd的一半,然后cwnd设置为一半然后再加三
    TCP Tahoe版本中,拥塞算法只有三个,慢启动、拥塞避免、快速重传。在收到3个重复的ACK时候,将ssthresh设置为当前cwnd的一半,然后cwnd设置1(进入慢启动)。(ssthresh = cwnd / 2, cwnd = 1)。
    早期TCP拥塞控制.webp
    TCP Tahoe版本中,拥塞算法在原来的基础上增加了一个快速恢复,就是在收到3个重复的ACK时候,发送端将ssthresh设置为当前cwnd的一半,然后cwnd设置为一半然后再加三。(ssthresh = cwnd / 2,cwnd = cwnd / 2 + 3)。

  • 相关阅读:
    Springboot+mybatis-plus微信支付
    8.idea 使用 docker 构建 java web 项目
    【Java】Java生成PDF工具类
    【面试题】有了Docker为啥还需要k8s?
    JPA在事务结束时自动更新查询数据
    Qt项目移植到mac上一些问题汇总
    【0228】syslogger日志收集器工作原理【初始化配置参数篇】
    Seata-TCC模式
    MINet-测试算法性能-计算复杂度(GMac)、模型大小(M)、计算速度(FPS)
    redis cluster伪集群搭建及应用
  • 原文地址:https://blog.csdn.net/Roger_Spencer/article/details/133973102