计算机网络-------重传、TCP流量控制、拥塞控制

时间:2024-10-08 13:40:52

重传、滑动窗口、流量控制、拥塞避免

重传机制

超时重传

发送方在发送数据时会启动一个定时器,当超过指定的时间之后,还没接收到接收方的ACK确认应答报文,就会重传该数据

快重传

当发送方收到接收方三个连续的ack之后说明发送方发送的报文丢失了,此时发送方就重传该丢失报文

SACK

快重传出现的问题:因为出现三个连续相同的ACK确认报文之后会进行重传,但是我们只是重传三个连续ACK的那个报文尼?还是重传三个连续ACK报文之后的所有报文

  • 如果只是重传目前检测出来丢失的报文,假设我们这个丢失报文之后还有报文丢失,那么后面就又会出现三个连续ACK然后再进行重传
  • 如果重传的是丢失报文之后的所有报文,那么对于没丢失的报文,又会出现重复传输现象,浪费资源

所以有了SACK(选择性的ACK)即出现三次相同连续ACK后,会进行快重传,不同的时这三次发送的数据都会被存放在缓冲区中,然后只需要告知发送方我们接收到的数据范围就可以了,最后发送方就可以只发送丢失的数据部分

Duplicate SACK机制

相比于SACK机制来说其实就是能让发送方知道是因为发送放的丢包造成的重传还是因为接收方确认时的ACK报文丢包而产生的重传

Duplicate SACK是建立在SACK(选择性确认)的基础上的,

如果是发送方丢包:发送方收到三个相同的ACK进行快重传后,发送重传报文,如果重传的报文被ACK了说明是因为缺失数据导致重传的,重传的数据刚好补上窗口的范围,然后当丢包的报文是因为网络延迟导致的,后面再到达接收方,那么接收方会回复下一个ACK报文并且携带SACK重复提交报文范围给发送方

接收方ACK报文丢失:

发送方传数据报文丢失或者报文延迟:

滑动窗口:

主要由三个指针来维持

第一个指针之前表示:发送方已经发送并且收到了ack的数据

第一个指针到第二个指针表示:已经发送数据但是还没收到ack的数据

第二个指针到第三个指针表示:在窗口内的还没发送的数据

第三个指针之后的表示,不在窗口内,未发送的数据

流量控制

流量控制的核心

对于流量控制来说其实核心就是通过滑动窗口来完成的,其发送窗口发送的大小需要根据接收方可接受的窗口大小来制定,而接收方的滑动窗口是依赖于操作系统的所给的缓存的。接收方的窗口是基于接收方的实际情况来动态制定的,

  • 比如接收方这台服务器的处理数据能力,即当缓冲区(滑动窗口)中有数据比如接收方本来窗口长度为200,现接收到了100长度的数据,但是此时因为操作系统比较繁忙,所以只能处理40长度数据,那么可用的窗口就只有160了(如果处理的快,那么就还是200)
  • 比如现在服务器资源紧张只用给滑动窗口150的长度作为缓冲区,那么接收窗口的最大长度就只有150了
窗口关闭后可能带来的问题

什么叫窗口关闭:其实就是接收方此时没有能力接收数据了,就放松给发送方自己的滑动窗口可用大小为0,那么接收方就进入等待状态,等待接收方发下一个报文过来通知自己发送方有能力接收报文了,然后后续再进行传输数据

可能出现的问题:当接收方此时有能力接受数据了,此时发送相应的报文给发送方包括,期望收到的序列号(ACK报文),以及当前窗口的接收能力,但是此时这个报文丢包了,此时ACK报文丢包又不会进行超时重传,那么对于接收方来说会一直等待发送方发送数据,而对于发送放来说也一直在等待接收方发送报文来通知自己,就会造成死锁状态(双方都进行死等)

TCP解决窗口关闭的方法:

为了解决这种因为关闭窗口死锁问题,TCP会为每个连接设置一个计时器,当出现关闭窗口的时候(接收方的接收窗口长度为0的时候),就会启动定计时器,如果计时器超时了,发送方就会发送一个窗口探测报文,而对方在确认这个探测性报文的时候,会给出自己现在接收窗口的大小。

  • 如果发送过来的窗口大小还是0那么重置计时器,等到了规定的时间再重新发送探测性报文
  • 如果发送过来的接收窗口不为0,那么开始正常的接发数据

糊涂窗口综合症问题

是什么:其实就是接收方此时缓冲区滑动窗口中只有几个字节的空闲容量就将器窗口大小告知给发送方,然后发送方根据其窗口大小就直接传输数据,导致每次传输的有效数据只有几个字节,这是比较浪费资源的因为,对于有效数据的传输还需要给他加上一系列的头部,比如TCP、IP头部,最终只是传输几个字节,自然就比较浪费资源

造成的原因主要有如下两个:

  • 1、接收方通知发送方一个比较小的窗口
  • 2、发送方发送一个比较小的数据

根据造成的原因,解决方法如下(两个组合起来才能解决)

  • 1、让接收方不要通知一个小窗口给对方
    • 当接收方的窗口大小,小于(最大报文长度MSS,缓冲区大小的一半)时,就告知给发送方一个长度为0的接收窗口,等此时接收方的接收窗口大小大于最大报文长度或者大于缓冲区大小的一半时才告知发送方发送数据过来
  • 2、发送方避免发送效数据
    • 发送方主要采用Nagle算法:当满足下面两个条件是才进行发送数据给接收方
      • 条件一、发送方窗口大小和数据大小都大于MSS最大报文长度时
      • 条件二:收到之前数据的ack确认报文

综上TCP中解决糊涂窗口综合症是通过:接收方不发送小窗口+发送方使用Nagle算法来解决

拥塞控制

为什么要有拥塞控制

流量控制只是避免发送发的数据填满接收放的缓存,即考虑的只是个体,但是计算机网络是处于一个共享的环境中。因此也有可能因为其他主机的通信使得网络拥堵。(可以理解为车辆和公共交通)

网络拥堵时如果只是考流量控制来调节,那么势必频繁出现超时重传、大量丢包象限,会造成网络越来越拥堵,因此需要拥塞控制来进行协调

我的理解是流量控制偏向于控制个体,而拥塞控制则偏向于宏观调控

整体调控图(重要)

注意这里的窗口大小指的是MSS即最大报文长度

只要看懂这个图其实就理解了拥塞控制的核心了

TCP刚建立连接后

  • 进行慢启动算法:即每收到一个ACK,拥塞窗口cwnd就+1,因此拥塞窗口cwnd会成指数形式增加
  • 当cwnd>=慢启动门限ssthresh时切换为拥塞避免算法,即拥塞窗口大下增速将为每次增加1,成线性增长
  • 然后如果过程中出现了超时重传,那么说明网络出现了拥塞,那么执行如下操作
    • 将慢启动门限ssthresh设置为当前拥塞窗口cwnd的一半
    • 然后将拥塞窗口cwnd置为1
    • 最后使用慢启动算法控制拥塞窗口cwnd
  • 接着如果过程中出现了三个连续的ACK,执行快恢复算法:
    • 首先将慢启动门限ssthresh设置为当前窗口的一般
    • 并且将拥塞窗口cwnd也设置为原来拥塞窗口的一半+3(收到几个重复的ACK就加几)
    • 让后当收到了新的ACK的时候说明丢包数据重传接收成功,那么重新将当前窗口设置为刚才设置的慢开始门限大小,
    • 最后执行拥塞避免算法

其中自己额外的理解:

对于慢启动来说:其实就是连接刚开始时为了较为快速的增加调整拥塞窗口大小的作用,因为连接刚建立其实可以认为网络比较通畅

然后打到一个阈值(慢启动门限)后就将原来指数级增长进行降速使用拥塞避免来进行线性增长,去试探当前网络的极限(慢慢试探的感觉)
然后如果网络中出现了较为严重的拥塞阻塞,比如出现了超时重传而发送方收到了三个重复的ACK,那么就直接大幅度按调整拥塞窗口大小

而如果收到的是三个连续的ACK那么就说明虽然出现了丢包现象,当时还能收到重复的ACK,可以说明其实网络并不是特别拥堵,所以使用快恢复算法进行调节

而对于快恢复中每收到一个重复的ACK,拥塞窗口大小就+1,我的理解是因为,重复的ACK已经收到了,相当于这条信息已经从网络中移除了,那么就再增加窗口大下,然后当收到新的ACK后那么就开始正常的拥塞避免来进行控制

慢开始

慢启动其实就是TCP在刚建立连接之后,首先有个慢启动过程,其实就是一点一点的提高发送数据包的数量

核心规则:就是每收到一个ack就增加一个发送请求,拥塞窗口cwnd就+1

比如:

第一批请求个数为1

第二批请求个数为2:(因为收到第一批的ACK,然后每收到一个ACK拥塞窗口+1)

第三批请求个数为4:。。。。。

拥塞窗口大小成指数形式增加

指数形式增加增速很快,如果一直下去很快就会造成网络拥堵,所以当拥塞窗口cwnd>=慢启动门限ssthresh时就切换为拥塞避免算法

一般的慢启动门限大小一般时65535个字节(2^16)

拥塞避免算法

当拥塞窗口cwnd 超过慢启动门限ssthresh就会进入拥塞避免算法。
一般来说ssthresh的大小 65535字节。
那么进入拥塞避免算法后,它的规则是:每当收到一个ack时,cwnd增加1/cwnd.
接上前面的慢启动的栗子,现假定ssthresh为8

  • 当8个ack应答确认到来时,每个确认增加1/8,8个ack确认cwnd一共增加1,于是这一次能够
    发送9个mss大小的数据,变成了线性增长。
拥塞发生

当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种

  • 超时重传
  • 快速重传

这两种使用的拥塞发送算法是不同的,接下来分别来说说。

  • 发生超时重传的拥塞发生算法:当发生了超时重传,则就会使用拥塞发生算法。ssthresh和cwnd的值会发生变化:
    • ssthresh设为cwnd/2
    • cwnd重置为1(是恢复为cwnd初始化值,我这里假定cwnd初始化值1)

  • 发生快速重传的拥塞发生算法
    还有更好的方式,当接收方发现丢了一个中间包的时候,发送三次前一个包的ack,于是发送端就会快速地重传,不必等待超时再重传。
    Tcp认为这种情况不严重,因为大部分没丢,只丢了一小部分,则sthresh和cmnd ssthresh和cwnd变化如
    • cwnd-cwnd/2,也就是设置为原来的一半;
    • ssthresh=cwnd
    • 进入快速恢复算法

快恢复:
    • 首先将慢启动门限ssthresh设置为当前窗口的一半
    • 并且将拥塞窗口cwnd也设置为原来拥塞窗口的一半+3(收到几个重复的ACK就加几)
    • 让后当收到了新的ACK的时候说明丢包数据重传接收成功,那么重新将当前窗口设置为刚才设置的慢开始门限大小,
    • 最后执行拥塞避免算法

 目前已更新系列:

当前:计算机网络-------重传、TCP流量控制、拥塞控制

实习期间git的分枝管理以及最常用的命令-****博客

Redis高级-----持久化AOF、RDB原理

Redis高级---面试总结5种数据结构的底层实现

Redis高级----主从、哨兵、分片、脑裂原理-****博客

Redis高级---面试总结内存过期策略及其淘汰策略

计算机网络--面试知识总结一

计算机网络-----面试知识总结二

计算机网络--面试总结三(Http与Https)

计算机网络--面试总结四(HTTP、RPC、WebSocket、SSE)-****博客

知识积累之ThreadLocal---InheritableThreadLocal总结

并发编程之----线程池ThreadPoolExecutor,Excutors的使用及其工作原理

声明:这里的知识总结是参考学习:详细学习可参考4.2 TCP 重传、滑动窗口、流量控制、拥塞控制 | 小林coding (xiaolincoding.com)