HarmonyOS-ArkUI Web控件基础铺垫1-HTTP协议-数据包内容-CSDN博客
HarmonyOS-ArkUI Web控件基础铺垫2-DNS解析-CSDN博客
HarmonyOS-ArkUI Web控件基础铺垫3--TCP协议- 从规则本质到三次握手-CSDN博客
HarmonyOS-ArkUI Web控件基础铺垫4--TCP协议- 断联-四次挥手解析-CSDN博客
HarmonyOS-ArkUI Web控件基础铺垫5--TCP协议- 动画展示超时重传,滑动窗口,快速重传-CSDN博客
之前我们已经对TCP的三握四挥,重传, 和滑动窗口的推理过程进行了讲解,本文的流畅理解均建立在以上三篇文章涉及的内容之上。如果对TCP不太了解,建议直接从HarmonyOS-ArkUI Web控件基础铺垫3--TCP协议- 从规则本质到三次握手-CSDN博客开始阅读。
流量控制与拥塞控制关系
这两个概念我们一块简略解释下。因为流量控制和拥塞控制有联系。我们需要在最初就要了解为什么滑动窗口总是和流量控制,拥塞控制一块出现。
流量控制:就是控制发送方的发送速率,不要太快,让接收方来得及处理数据。通常情况下我们希望数据发送的越快越好,但是实际上如果发送方发送的数据太快,接收方可能来不及接收,就会造成数据的丢失。具体操作是,利用算法,算出流量控制的窗口大小,从而影响滑动窗口大小,来调整发送接收速率。
拥塞控制:就是防止过多的数据注入到网络中,导致网络过载。注意流量控制欲拥塞控制的区别,流量控制一般是点对点的控制。而拥塞控制是一个全局性的过程,涉及所有的主机和路由器等待。具体操作依然是利用算法,算出拥塞控制的窗口大小,从而影响滑动窗口的大小。
流量控制与拥塞控制的协作方式如图
为什么要进行流量控制
流量控制的由来我们前文实际上介绍了一点。滑动窗口机制就是实现流量控制的重要机制。下面是最原始的流水线协议传输过程,没有流量控制:
这种是比较野蛮的传输方式,发送端不知道接收端的处理能力,就发送数据,如果接收方的处理能力很差,会导致很多数据包被浪费,是很不合理的。其中接收端处理能力不行有多方原因:
- 接收端本身硬件就不支持快速的传输。它可能处于低带宽网络环境中。
- 接收端接收到数据,但是数据消费太慢。例如您用HTTP协议读取下载内容,结果由于业务逻辑有不是VIP就限制速度的需求,此时您可能会针对非VIP用户读取的很慢。那么就必然导致接收端的数据消费的慢。
- 接收端缓冲区配置不足,无法容纳突发流量或者持续高吞吐数据。等等。
滑动窗口的机制,便处理了这种问题。 在HarmonyOS-ArkUI Web控件基础铺垫5--TCP协议- 动画展示超时重传,滑动窗口,快速重传-CSDN博客一文中如何演进的我们已经讲过。在这里强调的事,流量控制的手段就是滑动窗口。
滑动窗口运作方式
在讲流量控制的算法前,我们完善一下滑动窗口机制。在上篇文章里我们着重讲了滑动窗口是怎么演化而来的,但是对于滑动窗口的实现方式提及的很少。按照自上而下的学习方式,我们在这里补全滑动窗口的细节。
滑动窗口AB端同步流程
对于滑动窗口可以分为两类:
- 发送窗口(SWND) 接收窗口(RWND)
- 双方互为发送端。 所以其实每一方都存在一个发送窗口和接收窗口。
滑动窗口协议之间的同步,主要就是一方的发送方窗口与另一方接收窗口大小的组合策略。
TCP滑动窗口的大小同步是由接收方接收窗口主导,发送方发送窗口自适应的动态过程。其核心机制围绕接收方的缓冲区状态和网络拥塞状况展开。
TCP协议的窗口大小在每一个数据包中都会见到。无论是发送方还是接收方,无论是发送的数据包还是ACK,全部都要带。这个窗口的大小值,我们称为 rwnd。存储的位置如图所示,位于WIndow Size字段中。单位为字节!我们在以后的图中,会以 rwnd值来代表窗口大小。
窗口同步方式如图所示:
- 在三次握手的时候,第一次和第二次握手双方发送的数据包rwnd都是2000
- 当接收端接收到前四个数据包数据的时候发现自身数据消费慢,便改动接收窗口为1000,并跟随ACK发出去
- 发送端接受到ACK,根据ACK中的窗口值,1000, 调整发送端窗口的大小为1000. 并将窗口的起始位置定位到ACK中的seq映射的位置。
窗口为0时
有时候接收方的接收窗口大小会为0,比如:
- 接收方处理能力不足,接收缓冲区满
- 接收方的应用层延迟处理,导致读取逻辑阻塞。等等
此时接收方的接收窗口大小会变为0,会依然发送ACK包使得发送方调整发送窗口为0.当发送方发送窗口是0的时候,便不再发送数据了。
此时,发送端会进行零窗口探测。发送端内部会启动一个持续计时器,默认30-60s, 定期发送数据内容为1字节的探测包来询问接收方目前的窗口状态。接收方接收到探测包便返回最新的rwnd值。就这样重新起开了窗口。
与此同时,当接收方内部的缓存得到释放后,会向接收方发送一个普通的ACK包,ACK包中的窗口大小为当前支持的窗口大小。这样发送方接收并解析之后就可以正常发送数据了。
滑动窗口如何滑动
简单的理解,滑动窗口的滑动位置,为收到的ACK中 ack中所映射的位置。向下移动不就好喽?--如果这么简单,那不必专门写一个小节😂。
我们探讨的是,滑动窗口在数据结构上是怎么个移动法。
我们知道,当TCP完成握手的时候,发送端和接收端分别都会开辟缓冲区。对于接收端,这个缓冲区是接收缓冲区。这个大小我查资料,Linux默认为87KB,最小值为4KB,最大值可扩展至6M(高带宽场景).
然而我们传输的数据通常远远大于87KB!滑动窗口如果移动的时候,是按照从前到后这样移动,那肯定到了87KB就指针越界了!所以对于缓冲区的数据结构而言,必须要采取一种可以快速循环快速偏移的方式。来支持滑动窗口.
TCP采取的是环形缓冲区,来确保窗口可以循环复用内存边界。对于一个窗口而言其数据也有状态分类,如图所示。
尽管窗口会滑动,滑动的范围在于ACK的偏移量。但是窗口是在一个环形缓冲区中滑动的。你可以想象是一个转圈的图。
流量控制-rwnd的计算方式
rwnd是Receiver Window的缩写,就是接收窗口大小的意思。流量控制就是一个计算出rwnd的过程。上文中我们已经介绍了这个值。这个值之后会被参与计算最终接收端的窗口大小。
这个公式还是相当简单的。也能解释如果应用层停止read()数据,TCP干脆就不传输了,这种现象。因为应用层不read()数据,会导致应用层已读取的最后字节序号不动, 但是此刻滑动窗口却会不断地向后滑,这样未消费的数据就会越积越多,直到挤压的窗口空间为0!
这下如果有个下载需求告诉您非VIP用户,就要让他慢。知道切入点在哪里了吧😄😄
显然流量控制最为关注的就是接收端本地的缓存状况,以及数据消费能力。流量控制先告一段落。我们算出来rwnd基本就达到目的了。。
拥塞控制及cwnd值的计算方法
拥塞控制主要是用来
- 防止过多的数据注入到网络,避免网络中的路由器或链路过载
- 是一个全局性的过程,涉及到所有主机,路由器以及降低网络传输性能有关的所有因素。
如图所示:
当发送数据的设备比较多的时候,随着连接数量的增加,网络负载加重,则必然会导致拥堵。拥堵会引发发送端超时重传机制。如果大量的发重传包,则又会加重网络负担。
由于引发问题的设备较多,数量较大,而且传输过程中太多不可预测。所以拥塞控制的着力点并不在于统筹所有设备状况进行管理(事实上这种方式根本不可能实现)。 拥塞控制主要是通过自身试探的方式,来探测出当前的传输情况,从而计算拥塞窗口的大小。想象如果每台设备都探测,则这种机制可以尽量保障他们都能测算出比较科学的数值。
拥塞窗口变量cwnd的计算方式
拥塞窗口计算的是发送端的拥塞窗口大小。与流量控制不一样,流量控制计算的是接收端的接收窗口。 拥塞窗口是在发送端计算的。发送端在发送数据的时候的确需要知道目前的网络情况。
拥塞窗口的计算方式比较普遍的是采用Reno算法。其思路如下:
RTT
在了解Reno算法前,先了解下RTT。因为算法需要用到这个。
RTT全称 round trip time, 是衡量网络传输性能的核心指标。指的是 从发送方发送数据包开始,到收到接收方对改数据包的确认ACK所经历的时间。这段时间里涵盖了以下环节的耗时
- 传播时延:信号在物理链路上的传输时间(与距离和介质相关,如光纤 vs 卫星)
- 排队时延:数据包在路由器/交换机缓存中的等待时间(受网络拥塞程度影响)
- 处理时延:发送/接收端系统处理数据的时间(如内核协议栈处理)
如何判断网络拥塞
TCP通过丢包来判断网络是否出现拥塞的。具体的丢包场景为
- 1 发送端出现超时丢包,发送端超时重传中的计时器,时间到了,还没有收到对应的ACK,则此时认为丢包了。这种情况往往说明网络拥塞情况是较为严重。
- 2 发送端收到3个连续一样的ACK包,此时代表虽然接收端没有接收到数据,但是网络情况比上一个情况好一些。轻微的有些拥堵。
Reno算法
Reno算法整体不复杂,没有涉及到超级难的公式。主要就是梳理逻辑。针对不同的情况主要包含四种操作。其中有的我们上篇文章已经讲过:
- 慢启动
- 拥塞避免
- 快速重传 详见HarmonyOS-ArkUI Web控件基础铺垫5--TCP协议- 动画展示超时重传,滑动窗口,快速重传-CSDN博客
- 快速恢复
Reno算法的整体特征为,前期窗口呈指数级增长,之后再线性增长,探测到一定边缘后便检测到拥塞,从而再压低值,使得发送的窗口在一个平稳的波动中。
具体操作是
- 首先开始慢启动操作, 慢启动的规则是:最初设定cwnd值为1,则发送窗口因为取 cwnd 和 rwnd的最小值而选择是1. 发送端就会发送一个数据包出去,自此每一次传输轮次,窗口大小都会变成之前的 2倍。 所以慢启动的窗口大小是按照指数来增长的。
- 但是指数增长后期会爆发式上涨,用来设置窗口大小肯定不合适。Reno给慢开始设定了一个慢启动门限,叫ssthresh。当cwnd超过这个值之后,就切换成第二种操作--拥塞避免算法。
- 拥塞避免算法具有线性缓慢增长的能力,其具体的规则是,拥塞窗口每次经过一个RTT,大小则会加1。
- 无论是慢启动环节,还是拥塞避免启动环节,只要发送端判断出来网络拥塞,就会把ssthresh设置为拥塞时发送窗口的一半。
- 如果阻塞原因是超时重传引发的,则代表网络状况比较糟糕,则cwnd值设置为1,重新执行慢启动操作。
- 如果阻塞原因是收到了三个连续一样的ack, 则代表出现了轻微拥堵,则缩小幅度也没必要像超时丢包那么大,同时也可以进行缓慢的增长。于是会开启两个操作
- 执行快速重传操作,前文我们讲过。
- 开始启动快速恢复算法:快速启动算法也就是避免cwnd值直接设置为1,假定这个值已经增长到了ssthresh的前提下,临时抬高窗口大小,每当ACK重复包来一次,便抬高1.直到收到下一个新的ACK,窗口值直接降到ssthresh大小,再按照拥塞避免的操作执行。
我们根据上述的流程看一下下图便可以理解了:
小结
本文主要描述了
- 针对接收方流量控制的数据同步流程与流量窗口 rwnd 计算方式。
- 针对发送方拥塞控制的计算算法,及 cwnd 计算方式。
我们回顾文章开始的,实际发送方发送窗口的大小则按照以下规则计算。