• 发布时间:2024-10-14 10:24:59•浏览次数:170
作者 | 小林coding
来源 | 小林coding(ID:CodingLin)
相信大家都知道 TCP 是一个可靠传输的协议,那它是如何保证可靠的呢?
为了实现可靠性传输,需要考虑很多事情,例如数据的破坏、丢包、重复以及分片顺序混乱等问题。如不能解决这些问题,也就无从谈起可靠传输。
那么,TCP 是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。
今天,将重点介绍 TCP 的重传机制、滑动窗口、流量控制、拥塞控制。
提纲
TCP 实现可靠传输的方式之一,是通过序列号与确认应答。
在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。
正常的数据传输
但在错综复杂的网络,并不一定能如上图那么顺利能正常的数据传输,万一数据在传输过程中丢失了呢?
所以 TCP 针对数据包丢失的情况,会用重传机制解决。
接下来说说常见的重传机制:
超时重传
快速重传
SACK
D-SACK
TCP 会在以下两种情况发生超时重传:
数据包丢失
确认应答丢失
超时重传的两种情况
超时时间应该设置为多少呢?
我们先来了解一下什么是 RTT(Round-Trip Time 往返时延),从下图我们就可以知道:
RTT
RTT 就是数据从网络一端传送到另一端所需的时间,也就是包的往返时间。
超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。
假设在重传的情况下,超时时间 RTO 「较长或较短」时,会发生什么事情呢?
超时时间较长与较短
上图中有两种超时时间不同的情况:
精确的测量超时时间 RTO 的值是非常重要的,这可让我们的重传机制更高效。
根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。
RTO 应略大于 RTT
至此,可能大家觉得超时重传时间 RTO 的值计算,也不是很复杂嘛。
好像就是在发送端发包时记下 t0 ,然后接收端再把这个 ack 回来时再记一个 t1,于是 RTT = t1 – t0。没那么简单,这只是一个采样,不能代表普遍情况。实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值。我们来看看 Linux 是如何计算RTO的呢?
估计往返时间,通常需要采样以下两个:
需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。
除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况。
RFC6289 建议使用以下的公式计算 RTO:
RFC6289 建议的 RTO 计算
其中 SRTT 是计算平滑的RTT ,DevRTR 是计算平滑的RTT 与 最新 RTT 的差距。
在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。别问怎么来的,问就是大量实验中调出来的。
如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍。
也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?
于是就可以用「快速重传」机制来解决超时重发的时间等待。
TCP 还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。
快速重传机制,是如何工作的呢?其实很简单,一图胜千言。
快速重传机制
在上图,发送方发出了 1,2,3,4,5 份数据:
第一份 Seq1 先送到了,于是就 Ack 回 2;
结果 Seq2 因为某些原因没收到,Seq3 到达了,于是还是 Ack 回 2;
后面的 Seq4 和 Seq5 都到了,但还是 Ack 回 2,因为 Seq2 还是没有收到;
发送端收到了三个 Ack = 2 的确认,知道了 Seq2 还没有收到,就会在定时器过期之前,重传丢失的 Seq2。
最后,接收到收到了 Seq2,此时因为 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。
所以,快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传之前的一个,还是重传所有的问题。
比如对于上面的例子,是重传 Seq2 呢?还是重传 Seq2、Seq3、Seq4、Seq5 呢?因为发送端并不清楚这连续的三个 Ack 2 是谁传回来的。
根据 TCP 不同的实现,以上两种情况都是有可能的。可见,这是一把双刃剑。
为了解决不知道该重传哪些 TCP 报文,于是就有 SACK 方法。
还有一种实现重传机制的方式叫:SACK( Selective Acknowledgment 选择性确认)。
这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将缓存的地图发送给发送方,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制,通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。
选择性确认
如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。
Duplicate SACK 又称 D-SACK,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
下面举例两个栗子,来说明 D-SACK 的作用。
栗子一号:ACK 丢包
ACK 丢包
「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499)
于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」 3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着 D-SACK。
这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。
栗子二号:网络延时
网络延时
数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」;
所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。
这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。
可见,D-SACK 有这么几个好处:
可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
可以知道是不是「发送方」的数据包被网络延迟了;
可以知道网络中是不是把「发送方」的数据包给复制了;
在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。
引入窗口概念的原因
我们都知道 TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了, 再发送下一个。
这个模式就有点像我和你面对面聊天,你一句我一句。但这种方式的缺点是效率比较低的。
如果你说完一句话,我在处理其他事情,没有及时回复你,那你不是要干等着我做完其他事情后,我回复你,你才能说下一句话,很显然这不现实。
按数据包进行确认应答
所以,这样的传输方式有一个缺点:数据包的往返时间越长,通信的效率就越低。
为解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。
那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。
假设窗口大小为 3 个 TCP 段,那么发送方就可以「连续发送」 3 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图:
用滑动窗口方式并行处理
图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通话下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答。
窗口大小由哪一方决定?
TCP 头里有一个字段叫 Window,也就是窗口大小。
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
所以,通常窗口的大小是由接收方的决定的。
发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。
发送方的滑动窗口
我们先来看看发送方的窗口,下图就是发送方缓存的数据,根据处理的情况分成四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口:
#1 是已发送并收到 ACK确认的数据:1~31 字节
#2 是已发送但未收到 ACK确认的数据:32~45 字节
#3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节
#4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节以后
在下图,当发送方把数据「全部」都一下发送出去后,可用窗口的大小就为 0 了,表明可用窗口耗尽,在没收到 ACK 确认之前是无法继续发送数据了。
可用窗口耗尽
在下图,当收到之前发送的数据 32~36 字节的 ACK 确认应答后,如果发送窗口的大小没有变化,则滑动窗口往右边移动 5 个字节,因为有 5 个字节的数据被应答确认,接下来 52~56 字节又变成了可用窗口,那么后续也就可以发送 52~56 这 5 个字节的数据了。
32 ~ 36 字节已确认
程序是如何表示发送方的四个部分的呢?
TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(需要做偏移)。
SND.WND、SND.UN、SND.NXT
SND.WND:表示发送窗口的大小(大小是由接收方指定的);
SND.UNA:是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。
SND.NXT:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。
指向 #4 的第一个字节是个相对指针,它需要 SND.UNA 指针加上 SND.WND 大小的偏移量,就可以指向 #4 的第一个字节了。
那么可用窗口大小的计算就可以是:
可用窗口大 = SND.WND -(SND.NXT - SND.UNA)
接收方的滑动窗口
接下来我们看看接收方的窗口,接收窗口相对简单一些,根据处理的情况划分成三个部分:
#1 + #2 是已成功接收并确认的数据(等待应用进程读取);
#3 是未收到数据但可以接收的数据;
#4 未收到数据并不可以接收的数据;
接收窗口
其中三个接收部分,使用两个指针进行划分:
RCV.WND:表示接收窗口的大小,它会通告给发送方。
RCV.NXT:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。
指向 #4 的第一个字节是个相对指针,它需要 RCV.NXT 指针加上 RCV.WND 大小的偏移量,就可以指向 #4 的第一个字节了。
接收窗口和发送窗口的大小是相等的吗?
并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。
因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
发送方不能无脑的发数据给接收方,要考虑接收方处理能力。
如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。
为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
下面举个栗子,为了简单起见,假设以下场景:
客户端是接收方,服务端是发送方
假设接收窗口和发送窗口相同,都为 200
假设两个设备在整个传输过程中都保持相同的窗口大小,不受外界影响
流量控制
根据上图的流量控制,说明下每个过程:
客户端向服务端发送请求数据报文。这里要说明下,本次例子是把服务端作为发送方,所以没有画出服务端的接收窗口。
服务端收到请求报文后,发送确认报文和 80 字节的数据,于是可用窗口 Usable 减少为 120 字节,同时 SND.NXT 指针也向右偏移 80 字节后,指向 321,这意味着下次发送数据的时候,序列号是 321。
客户端收到 80 字节数据后,于是接收窗口往右移动 80 字节,RCV.NXT 也就指向 321,这意味着客户端期望的下一个报文的序列号是 321,接着发送确认报文给服务端。
服务端再次发送了 120 字节数据,于是可用窗口耗尽为 0,服务端无法在继续发送数据。
客户端收到 120 字节的数据后,于是接收窗口往右移动 120 字节,RCV.NXT 也就指向 441,接着发送确认报文给服务端。
服务端收到对 80 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 321,于是可用窗口 Usable 增大到 80。
服务端收到对 120 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 441,于是可用窗口 Usable 增大到 200。
服务端可以继续发送了,于是发送了 160 字节的数据后,SND.NXT 指向 601,于是可用窗口 Usable 减少到 40。
客户端收到 160 字节后,接收窗口往右移动了 160 字节,RCV.NXT 也就是指向了 601,接着发送确认报文给服务端。
服务端收到对 160 字节数据的确认报文后,发送窗口往右移动了 160 字节,于是 SND.UNA 指针偏移了 160 后指向 601,可用窗口 Usable 也就增大至了 200。
前面的流量控制例子,我们假定了发送窗口和接收窗口是不变的,但是实际上,发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,而操作系统的缓冲区,会被操作系统调整。
当应用进程没办法及时读取缓冲区的内容时,也会对我们的缓冲区造成影响。
那操心系统的缓冲区,是如何影响发送窗口和接收窗口的呢?
我们先来看看第一个例子。
当应用程序没有及时读取缓存时,发送窗口和接收窗口的变化。
考虑以下场景:
客户端作为发送方,服务端作为接收方,发送窗口和接收窗口初始大小为 360;
服务端非常的繁忙,当收到客户端的数据时,应用层不能及时读取数据。
根据上图的流量控制,说明下每个过程:
客户端发送 140 字节数据后,可用窗口变为 220 (360 - 140)。
服务端收到 140 字节数据,但是服务端非常繁忙,应用进程只读取了 40 个字节,还有 100 字节占用着缓冲区,于是接收窗口收缩到了 260 (360 - 100),最后发送确认信息时,将窗口大小通过给客户端。
客户端收到确认和窗口通告报文后,发送窗口减少为 260。
客户端发送 180 字节数据,此时可用窗口减少到 80。
服务端收到 180 字节数据,但是应用程序没有读取任何数据,这 180 字节直接就留在了缓冲区,于是接收窗口收缩到了 80 (260 - 180),并在发送确认信息时,通过窗口大小给客户端。
客户端收到确认和窗口通告报文后,发送窗口减少为 80。
客户端发送 80 字节数据后,可用窗口耗尽。
服务端收到 80 字节数据,但是应用程序依然没有读取任何数据,这 80 字节留在了缓冲区,于是接收窗口收缩到了 0,并在发送确认信息时,通过窗口大小给客户端。
客户端收到确认和窗口通告报文后,发送窗口减少为 0。
可见最后窗口都收缩为 0 了,也就是发生了窗口关闭。当发送方可用窗口变为 0 时,发送方实际上会定时发送窗口探测报文,以便知道接收方的窗口是否发生了改变,这个内容后面会说,这里先简单提一下。
我们先来看看第二个例子。
当服务端系统资源非常紧张的时候,操心系统可能会直接减少了接收缓冲区大小,这时应用程序又无法及时读取缓存数据,那么这时候就有严重的事情发生了,会出现数据包丢失的现象。
说明下每个过程:
客户端发送 140 字节的数据,于是可用窗口减少到了 220。
服务端因为现在非常的繁忙,操作系统于是就把接收缓存减少了 100 字节,当收到 对 140 数据确认报文后,又因为应用程序没有读取任何数据,所以 140 字节留在了缓冲区中,于是接收窗口大小从 360 收缩成了 100,最后发送确认信息时,通告窗口大小给对方。
此时客户端因为还没有收到服务端的通告窗口报文,所以不知道此时接收窗口收缩成了 100,客户端只会看自己的可用窗口还有 220,所以客户端就发送了 180 字节数据,于是可用窗口减少到 40。
服务端收到了 180 字节数据时,发现数据大小超过了接收窗口的大小,于是就把数据包丢失了。
客户端收到第 2 步时,服务端发送的确认报文和通告窗口报文,尝试减少发送窗口到 100,把窗口的右端向左收缩了 80,此时可用窗口的大小就会出现诡异的负值。
所以,如果发生了先减少缓存,再收缩窗口,就会出现丢包的现象。
为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间在减少缓存,这样就可以避免了丢包情况。
在前面我们都看到了,TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。
如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
窗口关闭潜在的危险
接收方向发送方通告窗口大小时,是通过 ACK 报文来通告的。
那么,当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失了,那麻烦就大了。
窗口关闭潜在的危险
这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据,如不不采取措施,这种相互等待的过程,会造成了死锁的现象。
TCP 是如何解决窗口关闭时,潜在的死锁现象呢?
为了解决这个问题,TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。
如果持续计时器超时,就会发送窗口探测 ( Windowprobe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
窗口探测
如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器;
如果接收窗口不是 0,那么死锁的局面就可以被打破了。
窗口探查探测的次数一般为 3 此次,每次次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
要知道,我们的 TCP + IP 头有 40 个字节,为了传输那几个字节的数据,要达上这么大的开销,这太不经济了。
就好像一个可以承载 50 人的大巴车,每次来了一两个人,就直接发车。除非家里有矿的大巴司机,才敢这样玩,不然迟早破产。要解决这个问题也不难,大巴司机等乘客数量超过了 25 个,才认定可以发车。
现举个糊涂窗口综合症的栗子,考虑以下场景:
接收方的窗口大小是 360 字节,但接收方由于某些原因陷入困境,假设接收方的应用层读取的能力如下:
接收方每接收 3 个字节,应用程序就只能从缓冲区中读取 1 个字节的数据;
糊涂窗口综合症
每个过程的窗口大小的变化,在图中都描述的很清楚了,可以发现窗口不断减少了,并且发送的数据都是比较小的了。
所以,糊涂窗口综合症的现象是可以发生在发送方和接收方:
接收方可以通告一个小的窗口
而发送方可以发送小数据
于是,要解决糊涂窗口综合症,就解决上面两个问题就可以了
让接收方不通告小窗口给发送方
让发送方避免发送小数据
怎么让接收方不通告小窗口呢?
接收方通常的策略如下:
当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。
等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
怎么让发送方避免发送小数据呢?
发送方通常的策略:
使用 Nagle 算法,该算法的思路是延时处理,它满足以下两个条件中的一条才可以发送数据:
要等到窗口大小 >= MSS 或是 数据大小 >= MSS
收到之前发送数据的 ack 回包
只要没满足上面条件中的一条,发送方一直在囤积数据,直到满足上面的发送条件。
另外,Nagle 算法默认是打开的,如果对于一些需要小数据包交互的场景的程序,比如,telnet 或 ssh 这样的交互性比较强的程序,则需要关闭 Nagle 算法。
可以在 Socket 设置 TCP_NODELAY 选项来关闭这个算法
setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));
为什么要有拥塞控制呀,不是有流量控制了吗?
前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大….
所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。
于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。
为了在「发送方」调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念。
什么是拥塞窗口?和发送窗口有什么关系呢?
拥塞窗口 cwnd是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化的。
我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。
拥塞窗口 cwnd 变化的规则:
只要网络中没有出现拥塞,cwnd 就会增大;
但网络中出现了拥塞,cwnd 就减少;
那么怎么知道当前网络是否出现了拥塞呢?
其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了用拥塞。
拥塞控制有哪些控制算法?
拥塞控制主要是四个算法:
慢启动
拥塞避免
拥塞发生
快速恢复
TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提高发送数据包的数量,如果一上来就发大量的数据,这不是给网络添堵吗?
慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,就拥塞窗口 cwnd 的大小就会加 1。
这里假定拥塞窗口 cwnd 和发送窗口 swnd 相等,下面举个栗子:
连接建立完成后,一开始初始化 cwnd = 1,表示可以传一个 MSS 大小的数据。
当收到一个 ACK 确认应答后,cwnd 增加 1,于是一次能够发送 2 个
当收到 2 个的 ACK 确认应答后, cwnd 增加 2,于是就可以比之前多发2 个,所以这一次能够发送 4 个
当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就可以比之前多发 4 个,所以这一次能够发送 8 个。
慢启动算法
可以看出慢启动算法,发包的个数是指数性的增长。
那慢启动涨到什么时候是个头呢?
有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
前面说道,当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。
一般来说 ssthresh 的大小是 65535 字节。
那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。
接上前面的慢启动的栗子,现假定 ssthresh 为 8:
当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd 一共增加 1,于是这一次能够发送 9 个 MSS 大小的数据,变成了线性增长。
拥塞避免
所以,我们可以发现,拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些。
就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。
当触发了重传机制,也就进入了「拥塞发生算法」。
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
超时重传
快速重传
这两种使用的拥塞发送算法是不同的,接下来分别来说说。
发生超时重传的拥塞发生算法
当发生了「超时重传」,则就会使用拥塞发生算法。
这个时候,sshresh 和 cwnd 的值会发生变化:
ssthresh 设为 cwnd/2,
cwnd 重置为 1
拥塞发送 —— 超时重传
接着,就重新开始慢启动,慢启动是会突然减少数据流的。这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。
就好像本来在秋名山高速漂移着,突然来个紧急刹车,轮胎受得了吗。。。
发生快速重传的拥塞发生算法
还有更好的方式,前面我们讲过「快速重传算法」。当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;
进入快速恢复算法
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
正如前面所说,进入快速恢复之前,cwnd 和 ssthresh 已被更新了:
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;
然后,进入快速恢复算法如下:
拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了)
重传丢失的数据包
如果再收到重复的 ACK,那么 cwnd 增加 1
如果收到新数据的 ACK 后,设置 cwnd 为 ssthresh,接着就进入了拥塞避免算法
快速重传和快速恢复
也就是没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长。
资料:
[1] 趣谈网络协议专栏.刘超.极客时间
[2] Web协议详解与抓包实战专栏.陶辉.极客时间
[3] TCP/IP详解 卷1:协议.范建华 译.机械工业出版社
[4] 图解TCP/IP.竹下隆史.人民邮电出版社
[5] The TCP/IP Guide.Charles M. Kozierok.
[6] TCP那些事(上).陈皓.酷壳博客.https://coolshell.cn/articles/11564.html
[7] TCP那些事(下).陈皓.酷壳博客.https://coolshell.cn/articles/11609.html
今日福利
遇见大咖
由 CSDN 全新专为技术人打造的高端对话栏目《大咖来了》来啦!
CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛携手京东集团技术副总裁、IEEE Fellow、京东人工智能研究院常务副院长、深度学习及语音和语言实验室负责人何晓冬,来也科技 CTO 胡一川,共话中国 AI 应用元年来了,开发者及企业的路径及发展方向!
1.胶管老化、破裂或者脱落,这时候就要更新或者重新更换胶管;
2.炉头主火未点燃,这时候就要等燃气散尽后再点火;
3.阀体密封不严,这时候就要更换阀体;
4.阀体密封圈老化,这时候就要更换密封圈;
5.使用灶具的空间通风不好,这时候就要使用空间通风。
1、首先我们当然需要排除外在的因素导致的臭味,看看家里面使用的燃气灶以及有多久没有清洗清洁了,主要的问题就在于燃气灶的底部一起灶头的下面,是不是残留着大量的食物,导致变质而发出臭味,将整个燃气灶都清理一遍,包括灶头火盖上的网眼。
2、天然气本身是无色无味的,但是由于它的特性以及天然气的广泛使用率,出现事故的可能还是存在,主要表现在泄漏后的危险,而无色无味的天然气如果泄漏,我们肯定在第一时间无法察觉,那么怎么办呢?工程师们就想到了一种办法,往天然气中添加一种能够让人警觉的气体,而这种气体既不会影响燃烧,也不会产生危险,关键还容易让人察觉。
(此处已添加小程序,请到今日头条客户端查看)
于是天然气中人工添加的气体就是四氢噻吩,它的优势就是臭味很足、不容易消散、便于长距离输送、也不会有沉积物、关键不容易被水冲洗掉,由此这种作为警告使用的气体得到全世界天然气供应商的使用。
1、 在燃气灶使用过程中必须打开窗户或换气扇以保持室内空气的流通。
2、 在使用过程中,请不要用手接触灶具台面,以防烫伤。
3、 为了使定时器定时准确,旋钮先转动幅度大一点,然后再回转到想要的定时时间。当不使用定时器时,请把定时器按钮开关转到“ON”位置。
4、 当发现燃烧器火焰参差不齐时,可用刷子或钢针清除喷火器上的污垢。
5、 清洗灶具台面时,请用柔软的棉布或毛巾蘸取足量的中性清洁液。
6、 喷火器、火盖、锅架清洗完后,必须等到干燥后再正确安装。
7、 长时间不使用燃气灶,务必取出干电池;同时还要定期换干电池。
8、 橡胶管使用时间不得超过18个月。
*以上部分图文源自网络,侵删联系。
从国家层面的战略决策,到地方政府刺激消费的家电补贴,再到主流厂商的促销让利……一系列瞄准中国家庭老旧家电的换新迭代,正在消费市场掀起新一轮的换代潮,厨房电器也不例外。继洗碗机、集成灶、成套厨电等成为换代关键词后,占据厨电C位的战略品类——灶具也进入换代升级期。
上溯到柴火灶、土灶、铁炉的旧时代,灶具就已经是家家户户厨房的必备。直到今天,烟火气仍是中国厨房幸福的代名词,灶具也是厨房的最不可或缺。要想真正升级厨房体验,占据厨房标配C位的灶具,必然需要来一次彻底的换代更新。方太全新的HECB.D燃气灶就承担起了这一使命!
之所以是方太率先引爆这一潮流,背后的原因也很简单:深耕中国厨房近30年,最快洞察到用户需求,并进行一系列的产品和技术创新。更值得关注的是,秉承“创新的源泉是仁爱,创新的目标是幸福”理念,方太灶具以全新的“好火力、易清洁、好操控、更安全”四维标准,在重新定义好灶具的同时,更打造了“幸福厨房”的生活体验。
万事已备,灶具换新潮汹涌而来
“促进家电家具家装消费,鼓励有条件的地区对淘汰旧家电家具并购买绿色智能家电、环保家具给予补贴。”2020年度,来自国家的这一战略部署,再次为家电消费送上一份政策“礼包”。
如果梳理2019年以来相关的政策会发现,作为“扩大内需,促进消费”重要力量的老旧家电的换新,已被国家以及北京、广东、山东、广西、贵州等地方政府锁定并积极推动。
仅政策层面一项,就多次为家电更新迭代按下加速键。另一方面,则是伴随生活水平的快速提升,消费升级的大势让传统老旧产品无法满足用户新需求。正如商务部研究院流通与消费研究所副所长关利欣所说,政策利好、技术进步、居民收入提升等因素驱动下,用户对家电产品的升级需求渐趋明显。
对自始至终承担着中国厨房最重要烹饪角色、却常常被忽视的灶具而言,同样也迎来政策推动和需求升级等多重利好。从需求端来看,“家用燃气灶的火力不够大;智能化操作怎么才能更方便快捷?能不能保证灶具的使用更安全?都2020年了,还浪费这么多时间在厨房清洁上……”一系列用户对传统灶具的吐槽也在呼吁产业来一场迭代。
实际上,近年来,灶具行业在技术创新、功能升级、产品迭代等层面一直探索求变,尤其是行业领军者的方太更是如此。以全新方太HECB.D燃气灶这一明星产品最具代表性。立足中国用户使用体验,该产品给出了覆盖安全、清洁、火力、便捷等方面的全新定义,一经上市已迅速点燃用户的换新热情,成为这一波灶具迭代的主力军。
大火力≠好灶具,系统方案定义好厨火
说到好灶具的标准,大火力显然不是全部,只是使用体验的基础。方太新燃气灶以增压直喷系统配合首创的隔热聚能罩,热负荷达到5kw,有效火力高达3.5kW,热效率提高75%,而且燃烧时热量不会四散。正因如此,方太燃气灶赢得不少用户“火猛但不燎人,油温上升快、炒菜出锅快,而且小火煲汤更稳定”的使用反馈。
“我是想给父母换灶具,老人年纪大了总忘事,最怕的就是用起来忘了关火。”北京的刘女士对灶具的换代需求则更为明确——安全。有了方太HECB.D燃气灶:小火慢炖可以使用左右分控大屏定时器,不用操心关火问题;针对溢锅的状况有熄火保护装置;加上五重防爆面板、安全伞+双针点火、防回火罩等12项安全保护和童锁设计,是当前燃气灶安全使用的最高标准。
刘女士在了解方太灶具后,立马下单一台送到父母家中。而B站一位小有名气的美食博主,已经开始主动给粉丝安利这款产品了:“即开即燃,而且有七档可视化精控火,爆炒精炖都能精准掌握火候”;“操控方便,凭手感就能换挡。感觉我不像在炒菜,像在赛车”;“鸳鸯锅架设计,圆底锅、平底锅,我这么多锅今天终于找到好归宿了”、“烟灶连动,这光影套系是不是很炫酷”……
从点火到控火,再到各种细节设计,方太HECB.D燃气灶足以让美食博主为之倾倒。而其黑晶板的面板构造;聚能罩、盛液盘、锅支架功能三合一;聚能罩的凹槽设计;炉头檐式防堵火盖等等,不但防污还减少汤汁外流,大大减少清洁灶具的工作强度和难度。
对中国人来说,一桌菜代表家的味道,而背后的烹饪体验更是美好生活的一部分。显然,扎根中国厨房多年,方太早已对厨火有了更深刻的情感认知。因此,方太眼中的好灶具最先颠覆了“大火力就是好灶”的传统概念,而是从“火力、清洁、操控、安全”等更全面的维度思考灶具如何贴近生活,并且改变生活。
在方太全新定义下,厨电产业和中国厨房更新换代的聚光灯,也从解放双手的洗碗机、带来全新烹饪方式的微蒸烤一体机、更符合整体家装风格的嵌入式和集成灶等新品类和新趋势身上,聚焦到传统灶具身上。方太用一台好灶,正引爆新一轮厨电更新换代潮,更将持续温暖“万家厨火”。
2021年,尼泊尔一家餐馆的餐具未经过消毒就供应给客人,导致多人食物中毒。在日常生活中,餐具是人们必不可少的器具之一,但是餐具不消毒会对人们的健康和生命造成严重危害。未经过消毒的餐具可能会受到细菌和病毒的污染,将这些餐具用于食品的制作和食用,不仅会影响食品的口感和质量,还会引起食品中毒等健康问题。因此,消毒餐具是确保食品安全的基本措施之一。消毒柜可以利用高温蒸汽和紫外线等技术,快速杀灭细菌和病毒,有效保障餐具的卫生和安全。那么消毒柜哪个牌子好?消费者该如何选购呢?
消毒柜哪个牌子好?方太TM01
结构:消毒柜的外壳结构决定了消毒柜的整体稳定性和美观性。在选择消毒柜时,人们需要选择外壳结构稳定、材质坚固、外观美观的消毒柜,以保证消毒柜的使用寿命和外观效果。
面板:消毒柜的操作面板通常包括开关、温度调节器、定时器等。在选择消毒柜时,人们需要选择操作面板简单易用、功能齐全的消毒柜,这样可以提高消毒柜的使用效率和便捷性。
消毒柜哪个牌子好?方太TM01
容量:容量是选择消毒柜时需要关注的一个重要因素。人们需要根据餐具种类和大小选择消毒柜的容量。不同种类的餐具(如碗、盘、勺、叉、刀等)大小不同,需要根据实际情况选择不同容量的消毒柜。如果需要消毒大型餐具,那么需要选择容量较大的消毒柜,否则可能会影响消毒效果。
安全保护:消毒柜的安全保护是非常重要的,包括过温保护、漏电保护和运转保护等。在选择消毒柜时,人们需要选择具有完善的安全保护措施的消毒柜,以保障消毒柜的安全性和稳定性。
消毒柜哪个牌子好?方太TM01
随着科技的发展,消毒柜也在不断进行技术创新,提高消毒效率和安全性。其中,方太TM01采用360°脉冲快消科技,利用了光化学反应、闪照热效应和脉冲效应等多种技术手段,大大提高了消毒柜的效果和安全性。同时,这款消毒柜还自带自动感应杀菌系统,能够快速杀灭餐具表面的细菌和病毒。重要的是,这款消毒柜全程无臭氧味,人们可以更加放心地使用。地暖式均匀加热技术,将腔内温度迅速提升至75℃,在短时间内,该消毒柜能够对餐具进行全面的消毒和烘干,让餐具恢复清新和干燥。
消毒柜哪个牌子好?方太TM01无臭氧味+地暖式均匀加热技术+自动感应杀菌系统,能够快速杀菌和烘干餐具,同时还能让用户尽享清新体验。作为消毒柜行业的一款创新产品,相信它会给广大消费者带来更加便捷和健康的使用体验。
维修师傅服务态度很好,快速的解决了问题,维修速度很快很专业
不知道是因为什么原因,师傅上门来给我检查了,告诉我了是什么原因,什么问题,然后我就让他修了,修理的很好,价格收费什么的也很合理
师傅服务态度很好,按约定好的时间很准时到了,维修很专业,一会儿功夫就给修好了,现在终于可以正常运作了
师父上门维修特别快,很仔细很认真,工作态度端正,而且其他问题也能帮忙处理没有收其他费用很满意
预约了师傅马上来上门来了,费用还是可以接受的,师傅态度做事不错
有专业的技术的人员,都拥有专业的培训,服务质量好,态度满意,价格合理
收费合理,师傅上门准时。态度挺好
找了师傅上门,检查后说是线路故障了,换了一下,半个小时就修好了,很专业
下单后师傅很快就联系我了,跟师傅电话聊好价格就上门了,换了显示面板、目前没有再继续乱响。
已修好,师傅服务周到,态度很好