vlambda博客
学习文章列表

TCP协议探究(三):RTT、滑动窗口和阻塞处理

点击👆蓝色“ 深入原理”,关注并“设为星标

技术干货,第一时间推送

1 RTT算法

1.1 概述

  • 上一节说了重传机制需要设置一个重传超时值(RTO,Retransmission TimeOut),RTO设长了,重发太慢;设短了,可能导致包没有丢,就重发了,可能导致雪崩效应(重发多,失败多,失败多,导致更多的重发...请参考: 暴风门事件)。

  • 那么该值怎么设置?

    • 由于一开始无法确定设置某个值,所以需要程序自动适应,动态地去设置

    • RTT,Round Trip Time,设置的参考值为数据报来回所需要的时间

1.2 经典算法

  • 采样最近几次的RTT

  • SRTT计算(Smoothed RTT):α (加权移动平均)取值在0.8 到 0.9之间
    $$
    SRTT = ( α * SRTT ) + ((1- α) * RTT)
    $$

  • 计算RTO:UBOUND为最大RTT(上限值),LBOUND为最小RTT(下限值),β 值一般在1.3到2.0之间
    $$
    RTO = min [ UBOUND, max [ LBOUND, (β * SRTT) ] ]
    $$

1.3 Karn / Partridge 算法(SRTT算法的优化)

  • 经典算法的问题:

    • 原始 + 重传 + ACK = 总时间作为RTO,算长了(特殊情况)

    • 重传 + ACK = 总时间作为RTO,算短了(特殊情况)

  • 该算法的最大特点:忽略重传,不把重传作为采样

1.4 Jacobson / Karels 算法

  • 忽略重传的问题:

    • 在某一时间,网络闪动,突然变慢了,产生了比较大的延时,这个延时导致要重传所有的包(RTO设置的比较小)

    • 但是由于重传不会重新更新RTO,导致一直丢包,一直重试了。

  • SRTT算法以及优化都逃不出RTT有一个大的波动的话,很难被发现,所以需要综合考虑。

  • 公式:

SRTT = SRTT + α (RTT – SRTT) —— 计算平滑RTT
DevRTT = (1-β)DevRTT + β(|RTT-SRTT|) ——计算平滑RTT和真实的差距(加权移动平均)
RTO= µ * SRTT + ∂ *DevRTT —— 神一样的公式
# 在Linux下,α = 0.125,β = 0.25, μ = 1,∂ = 4 —— nobody knows why, it just work.

2 滑动窗口

2.1 概述

  • 第一节说过TCP可靠性的保证之一就是流量控制(Flow Control)。

  • TCP需要知道现在网络的数据处理速度,才能更好防止丢包,而流量控制就是为了测量现在的网络数据处理速度的。

  • TCP报头有一个字段:窗口,该字段是接收端告知发送端自己的缓冲空间,防止发送端发送太快缓冲区溢出。

2.2 缓冲空间

TCP协议探究(三):RTT、滑动窗口和阻塞处理

  • 其实类似java的NIO中的ByteBuffer

  • 发送端:

    • LastByteWritten:上层应用可写入的位置

    • LastByteSent:正在发送的位置

    • LastByteAcked:已经收到ACK的位置

    • LastByteAcked ~ LastByteSent区间:表示已经发送但是未收到ACK的数据

    • LastByteSent ~ LastByteWritten区间:表示未发送出去的数据

  • 接收端:

    • LastByteRead:TCP缓冲区中读到的位置

    • NextByteExpected:收到的连续包的最后一个位置

    • LastByteRcved:收到的包的最后一个位置

    • NextByteExpected ~ LastByteRcved区间:未到达的数据区间

    • LastByteRead ~ NextByteExpected区间:已收到的数据区间

  • 接收端回复:

    • ACK中会汇报自己的Window = MaxRcvBuffer – LastByteRcvd – 1(只剩下这么多的空间能装新的数据)

    • 发送方会根据窗口来控制发送数据的大小,以保证接收方可以处理

2.3 滑动窗口

(1)发送方滑动窗口示意图:

TCP协议探究(三):RTT、滑动窗口和阻塞处理

  • #1 已收到ACK确认的数据

  • #2 已发送到未收到ACK确认的数据

  • #3 在窗口中未发出的(接收方还有空间)

  • #4 窗口以外的数据(接收方没有空间)

  • 其中#2 + #3的黑框就是滑动窗口

(2)发送方滑动后的滑动窗口示意图:

TCP协议探究(三):RTT、滑动窗口和阻塞处理

  • 红色是已经新收到ACK的数据

  • 绿色是新加入滑动窗口的数据

(3)接收端控制发送端的图示:

TCP协议探究(三):RTT、滑动窗口和阻塞处理

(4)Zero Window(坚持定时器实现):
  • 上图一个处理缓慢的Server将Client的TCP 滑动窗口给降到0.

  • 降到0之后,发送端发送Zero Window Probe包(ZWP)给接收方,让接收方ACK它的Window尺寸,一般发送3次,每次30~60秒。

  • 连续3次为0,有些TCP Client将发送RST把连接断开。

(5)Silly Window Syndrome(糊涂窗口综合症)
  • 由于处理缓慢的Server把TCP接收方的滑动窗口不断降低,导致滑动窗口很少(如:4个字节),此时发送端仍然义无反顾的发送。

  • MSS默认为536,有效数据才4个字节,带宽利用率1%不到,造成了巨大的浪费。

  • 窗口是为了控制传输过程中的速度。而MSS是为了控制TCP报文段大小。

  • MTU = MSS + TCP头(20字节) +IP头(20字节)。

  • 解决方案:
    • 等到Window Size >= MSS 或者 Data Size >= MSS

    • 收到之前发送数据的ACK包,它才会发送数据,否者继续积攒数据

    • 问题由Receiver引起,那么Receiver收到的数据导致window size(rwnd,receiver window)小于某个值,可以直接ack window size为0给Sender,这样就把window关闭了,也阻止Sender再发数据过来,等到Receiver处理了一些数据后window size大于等于MSS时,或者Receiver Buffer有一般为空(具体策略有很多),可以把window 打开让Sender发送数据过来。

    • 问题由Sender引起,使用延时处理,禁止大量小包发送。(打开之后无法使用telnet或者SSH这种交互性比较强的程序)

3 阻塞处理

3.1 概述

  • 前面说过网络波动时,TCP报文超时,引起重传,但是重传导致网络负担更大,可能导致网络更加繁忙。

  • 所以TCP不会这么自私,它在发现阻塞时,会主动让路(并不是直接停止发送TCP报文)。

  • 阻塞控制算法:

    • 慢启动

    • 阻塞避免

    • 阻塞发生时快速重传(上一节说过)

    • 快速恢复

3.2 慢启动算法

  • 程序刚刚加入网络时,一点一点提升速度。

  • 算法流程:

    • 连接建好的开始先初始化cwnd = 1(窗口),表明可以传一个MSS大小的数据

    • 每当收到一个ACK,cwnd++,线性上升

    • 每当过了一个RTT,cwnd = cwnd*2, 呈指数上升。

    • ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”

TCP协议探究(三):RTT、滑动窗口和阻塞处理

3.3 阻塞避免算法

  • 一般来说ssthresh的值是65535,单位是字节,当cwnd达到这个值时后

  • 算法流程:

    • 收到一个ACK时,cwnd = cwnd + 1/cwnd

    • 每过一个RTT时,cwnd = cwnd + 1

TCP协议探究(三):RTT、滑动窗口和阻塞处理

3.4 阻塞状态时的算法

当丢包的时候,有两种情况

  • 等到RTO超时,重传数据包,TCP认为该情况太糟糕了

    • sshthreash = swnd / 2

    • cwnd 重置为1

    • 进入慢启动算法

  • 快速重传算法,即收到3个重复的ACK就开始重传,无需等待RTO超时

    • cwnd = cwnd / 2

    • sshthresh = cwnd

    • 进入快速恢复算法——Fast Recovery

    • TCP Tahoe(代表版本)的实现和RTO超时一样。

    • TCP Reno的实现:

3.5 快速恢复算法(TCP Reno)

  • 这里就讲解TCP Reno版本的快速恢复算法,想了解更多请参考:TCP 的那些事儿(下)

  • 算法流程:

    • cwnd = sshthresh + 3 * MSS(3的意思是确认有3个数据包被收到了)

    • 重传Duplicated ACKs指定的数据包

    • 如果再收到 Duplicated ACKs,那么cwnd = cwnd +1

    • 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。

  • 缺点:由于3个重复的ACKs,并不代表只丢了一个数据包;如果丢了多个数据包,将导致RTO。

TCP协议探究(三):RTT、滑动窗口和阻塞处理

参考:https://coolshell.cn/articles/11609.html

https://www.cnblogs.com/linzhanfly/p/9969201.html

  

-深入原理-  

   知其然并知其所以然    

TCP协议探究(三):RTT、滑动窗口和阻塞处理