TCP详解(WireShark抓包分析TCP三次握手和TCP四次挥手)(二)

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: TCP详解(WireShark抓包分析TCP三次握手和TCP四次挥手)

为什么要客户端要等待2MSL呢?

为的是

  • (1)为了保证客户端发送的最后一个ACK报文段能够到达服务端。
  • (2)确保由于网络堵塞等原因迟到且客户端重发的消息在本次连接中作废掉,而不会进入下一次连接中。

当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;

如果客户端在2MSL(Maximum Segment Lifetime 最长报文段寿命)内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。

所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因。

当有大量的close_wait的时候服务端会出现什么情况?怎么去解决?

当有大量close_wait的时候,会占用服务器的fd,而一个服务器能打开的fd是有限的,如果无法分配fd了,就无法建立连接了。

那么如何去避免客户端断开异常的时候服务端close_wait呢?

我们可以针对性的去看那些没有关闭的close()的fd,比如我们mysql的连接一直没有close掉,那么我们需要在写代码的时候注意到

如果是用的Mybatis框架的时候,用的连接池,那么当一条连接用完之后就会自动返回连接池,减少连接的开启和关闭的时间。

如果用的是传统的JDBC的话,一定要记得写完后要记得调用close()方法关掉,如果客户端断开异常的时候容易造成服务端产生大量的close_wait。

参考:https://www.zhihu.com/question/298214130/answer/1090787813 「服务端close-wait或者time-wait状态过多会导致什么样的后果?」,参考了知乎博主「果冻虾仁」的回答。具体的可以点击链接访问他的论述。

TIME_WAIT会带来哪些问题呢?

TIME_WAIT 带来的问题注意是源于:一个连接进入 TIME_WAIT 状态后需要等待 2*MSL(一般是 1 到 4 分钟)那么长的时间才能断开连接释放连接占用的资源,会造成以下问题:

  • 作为服务器,短时间内关闭了大量的 Client 连接,就会造成服务器上出现大量的 TIME_WAIT 连接,占据大量的 tuple,严重消耗着服务器的资源。
  • 作为客户端,短时间内大量的短连接,会大量消耗的 Client 机器的端口,毕竟端口只有 65535 个,端口被耗尽了,后续就无法在发起新的连接了。

由于上面两个问题,作为客户端需要连本机的一个服务的时候,首选 UNIX 域套接字而不是 TCP。TIME_WAIT 很令人头疼,很多问题是由 TIME_WAIT 造成的,但是 TIME_WAIT 又不是多余的不能简单将 TIME_WAIT 去掉,那么怎么来解决或缓解 TIME_WAIT 问题呢?可以进行 TIME_WAIT 的快速回收和重用来缓解 TIME_WAIT 的问题。有没一些清掉 TIME_WAIT 的技巧呢?

什么是RST攻击?

RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误。假设有一个合法用户(1.1.1.1)已经同服务器建立了正常的连接,攻击者构造攻击的TCP数据,伪装自己的IP为1.1.1.1,并向服务器发送一个带有RST位的TCP数据段。服务器接收到这样的数据后,认为从1.1.1.1发送的连接有错误,就会清空缓冲区中建立好的连接。这时,如果合法用户1.1.1.1再发送合法数据,服务器就已经没有这样的连接了,该用户就必须重新开始建立连接。

RST攻击 这种攻击只能针对tcp,对udp无效。RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误。

产生原因

有四个条件可以产生RST包:

  1. 建立连接的SYN到达某端口,但是该端口上没有正在监听的服务
    如:IP为192.168.1.33的主机上并没有开启WEB服务(端口号为0x50),这时我们通过IE去访问192.168.1.33,通过Wireshark抓包,可以看到,对此SYN包的回复为RST。说明此服务器(即IP192.168.1.33)是存在的,不过其上并没有运行WEB Server(如apache)的程序
  2. 向处于TIME_WAIT状态的端口发起连接请求时

当客户端向服务器的某个端口发起连接,而服务器的该端口处于TIME_WAIT状态时,客户端会收到RST段

  1. 异常终止连接
    初始时,A和B已经建立了连接
    (1) 但是A使用socket选项SO_LINGER向B发送RST段,此时A缓冲区中所有排队等待的数据将都会丢弃(详细请看socket套接字选项详解的SO_LINGER)
    (2) B收到A发送来的RST段后,将异常的断开A和B之间的连接
  2. 处理半打开状态
    (1) A关闭或者异常终止了连接,但是B没有收到FIN段(比如网络发生了故障)。此时AB双方还维持着原来的连接,而A即使重启,也没有该连接的任何信息,将这种状态称为半打开状态。处于半打开状态的连接称为半打开连接。
    (2) B向A中写入数据,则A将回复RST段给B

总结一句话:向处于半打开状态的连接写入数据,则对方将回应一个RST段。

似乎挺恐怖,然而关键是。怎样能伪造成A发给B的包呢?这里有两个关键因素。源port和序列号。

一个TCP连接都是四元组。由源IP、源port、目标IP、目标port唯一确定一个连接。所以。假设C要伪造A发给B的包。要在上面提到的IP头和TCP头。把源IP、源port、目标IP、目标port都填对。这里B作为server,IP和port是公开的,A是我们要下手的目标。IP当然知道,但A的源port就不清楚了,由于这可能是A随机生成的。

当然。假设可以对常见的OS如windows和linux找出生成source port规律的话,还是可以搞定的。

序列号问题是与滑动窗体相应的,伪造的TCP包里须要填序列号,假设序列号的值不在A之前向B发送时B的滑动窗体内,B是会主动丢弃的。所以我们要找到能落到当时的AB间滑动窗体的序列号。这个能够暴力解决,由于一个sequence长度是32位,取值范围0-4294967296。假设窗体大小像上图中我抓到的windows下的65535的话,仅仅须要相除,就知道最多仅仅须要发65537(4294967296/65535=65537)个包就能有一个序列号落到滑动窗体内。RST包是非常小的,IP头+TCP头也才40字节,算算我们的带宽就知道这实在仅仅须要几秒钟就能搞定。

对付这种攻击也可以通过防火墙简单设置就可以了。建议使用防火墙将进来的包带RST位的包丢弃就可以了。

参考:https://baike.baidu.com/item/RST%E6%94%BB%E5%87%BB/5791572?fr=aladdin

参考:https://blog.csdn.net/weixin_36750623/article/details/84953067

TCP和UDP的差异点:

TCP的主要特点:

  1. TCP是面向连接的传输层协议。
  2. 每一条TCP连接只能有两个端点,每一条TCP连接都只能是点对点的(一对一)
  3. TCP提供可靠交付的服务。通过TCP连接传送的数据无差错、不丢失、不重复,且按序到达。
  4. TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。
  5. TCP是面向字节流的。TCP中的“流”指的是流入到进程或者从进程中流出的字节序列。

UCP的主要特点:

  1. UDP是无连接的,即发送数据之前是不建立连接的。
  2. UCP不保证可靠交付。
  3. UDP是面向报文的。
  4. UDP没有拥塞控制
  5. UCP支持一对一、一对多、多对一和多对多的交互通信。
  6. UDP首部的开销小,只有8个字节。比TCP的20个字节的首部要短。

四次挥手能不能变成三次挥手呢?

答案是可能的。TCP 是全双工通信,Cliet 在自己已经不会在有新的数据要发送给 Server 后,可以发送 FIN 信号告知 Server,这边已经终止 Client 到对端 Server 那边的数据传输。但是,这个时候对端 Server 可以继续往 Client 这边发送数据包。于是,两端数据传输的终止在时序上是独立并且可能会相隔比较长的时间,这个时候就必须最少需要 2+2= 4 次挥手来完全终止这个连接。但是,如果 Server 在收到 Client 的 FIN 包后,在也没数据需要发送给 Client 了,那么对 Client 的 ACK 包和 Server 自己的 FIN 包就可以合并成为一个包发送过去,这样四次挥手就可以变成三次了(似乎 linux 协议栈就是这样实现的)

Peer 两端,哪一端会进入 TIME_WAIT 呢?为什么?

相信大家都知道,TCP 主动关闭连接的那一方会最后进入 TIME_WAIT。那么怎么界定主动关闭方呢?是否主动关闭是由 FIN 包的先后决定的,就是在自己没收到对端 Peer 的 FIN 包之前自己发出了 FIN 包,那么自己就是主动关闭连接的那一方。对于疑症(4)中描述的情况,那么 Peer 两边都是主动关闭的一方,两边都会进入 TIME_WAIT。为什么是主动关闭的一方进行 TIME_WAIT 呢,被动关闭的进入 TIME_WAIT 可以不呢?我们来看看 TCP 四次挥手可以简单分为下面三个过程:

  • 过程一.主动关闭方发送 FIN;
  • 过程二.被动关闭方收到主动关闭方的 FIN 后发送该 FIN 的 ACK,被动关闭方发送 FIN;
  • 过程三.主动关闭方收到被动关闭方的 FIN 后发送该 FIN 的 ACK,被动关闭方等待自己 FIN 的 ACK。

问题就在过程三中,据 TCP 协议规范,不对 ACK 进行 ACK,如果主动关闭方不进入 TIME_WAIT,那么主动关闭方在发送完 ACK 就走了的话,如果最后发送的 ACK 在路由过程中丢掉了,最后没能到被动关闭方,这个时候被动关闭方没收到自己 FIN 的 ACK 就不能关闭连接,接着被动关闭方会超时重发 FIN 包,但是这个时候已经没有对端会给该 FIN 回 ACK,被动关闭方就无法正常关闭连接了,所以主动关闭方需要进入 TIME_WAIT 以便能够重发丢掉的被动关闭方 FIN 的 ACK。

TIME_WAIT 状态是用来解决或避免什么问题呢?

TIME_WAIT 主要是用来解决以下几个问题:

1)上面解释为什么主动关闭方需要进入 TIME_WAIT 状态中提到的: 主动关闭方需要进入 TIME_WAIT 以便能够重发丢掉的被动关闭方 FIN 包的 ACK。如果主动关闭方不进入 TIME_WAIT,那么在主动关闭方对被动关闭方 FIN 包的 ACK 丢失了的时候,被动关闭方由于没收到自己 FIN 的 ACK,会进行重传 FIN 包,这个 FIN 包到主动关闭方后,由于这个连接已经不存在于主动关闭方了,这个时候主动关闭方无法识别这个 FIN 包,协议栈会认为对方疯了,都还没建立连接你给我来个 FIN 包?,于是回复一个 RST 包给被动关闭方,被动关闭方就会收到一个错误(我们见的比较多的:connect reset by peer,这里顺便说下 Broken pipe,在收到 RST 包的时候,还往这个连接写数据,就会收到 Broken pipe 错误了),原本应该正常关闭的连接,给我来个错误,很难让人接受。

2)防止已经断开的连接 1 中在链路中残留的 FIN 包终止掉新的连接 2(重用了连接 1 的所有的 5 元素(源 IP,目的 IP,TCP,源端口,目的端口)),这个概率比较低,因为涉及到一个匹配问题,迟到的 FIN 分段的序列号必须落在连接 2 的一方的期望序列号范围之内,虽然概率低,但是确实可能发生,因为初始序列号都是随机产生的,并且这个序列号是 32 位的,会回绕。

3)防止链路上已经关闭的连接的残余数据包(a lost duplicate packet or a wandering duplicate packet) 干扰正常的数据包,造成数据流的不正常。这个问题和 2)类似。

清掉 TIME_WAIT 的奇技怪巧

可以用下面两种方式控制服务器的 TIME_WAIT 数量:

【1】修改 tcp_max_tw_buckets tcp_max_tw_buckets 控制并发的 TIME_WAIT 的数量,默认值是 180000。如果超过默认值,内核会把多的 TIME_WAIT 连接清掉,然后在日志里打一个警告。官网文档说这个选项只是为了阻止一些简单的 DoS 攻击,平常不要人为的降低它。

【2】利用 RST 包从外部清掉 TIME_WAIT 链接 根据 TCP 规范,收到任何的发送到未侦听端口、已经关闭的连接的数据包、连接处于任何非同步状态(LISTEN,SYS-SENT,SYN-RECEIVED)并且收到的包的 ACK 在窗口外,或者安全层不匹配,都要回执以 RST 响应(而收到滑动窗口外的序列号的数据包,都要丢弃这个数据包,并回复一个 ACK 包),内核收到 RST 将会产生一个错误并终止该连接。我们可以利用 RST 包来终止掉处于 TIME_WAIT 状态的连接,其实这就是所谓的 RST 攻击了。

TCP 的延迟确认机制

按照 TCP 协议,确认机制是累积的,也就是确认号 X 的确认指示的是所有 X 之前但不包括 X 的数据已经收到了。确认号(ACK)本身就是不含数据的分段,因此大量的确认号消耗了大量的带宽,虽然大多数情况下,ACK 还是可以和数据一起捎带传输的,但是如果没有捎带传输,那么就只能单独回来一个 ACK,如果这样的分段太多,网络的利用率就会下降。为缓解这个问题,RFC 建议了一种延迟的 ACK,也就是说,ACK 在收到数据后并不马上回复,而是延迟一段可以接受的时间,延迟一段时间的目的是看能不能和接收方要发给发送方的数据一起回去,因为 TCP 协议头中总是包含确认号的,如果能的话,就将数据一起捎带回去,这样网络利用率就提高了。

延迟 ACK 就算没有数据捎带,那么如果收到了按序的两个包,那么只要对第二包做确认即可,这样也能省去一个 ACK 消耗。由于 TCP 协议不对 ACK 进行 ACK 的,RFC 建议最多等待 2 个包的积累确认,这样能够及时通知对端 Peer,我这边的接收情况。Linux 实现中,有延迟 ACK 和快速 ACK,并根据当前的包的收发情况来在这两种 ACK 中切换。一般情况下,ACK 并不会对网络性能有太大的影响,延迟 ACK 能减少发送的分段从而节省了带宽,而快速 ACK 能及时通知发送方丢包,避免滑动窗口停等,提升吞吐率。关于 ACK 分段,有个细节需要说明一下,ACK 的确认号,是确认按序收到的最后一个字节序,对于乱序到来的 TCP 分段,接收端会回复相同的 ACK 分段,只确认按序到达的最后一个 TCP 分段。TCP 连接的延迟确认时间一般初始化为最小值 40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调整。

TCP 拥塞控制

谈到拥塞控制,就要先谈谈拥塞的因素和本质。

本质上,网络上拥塞的原因就是大家都想独享整个网络资源,对于 TCP,端到端的流量控制必然会导致网络拥堵。这是因为 TCP 只看到对端的接收空间的大小,而无法知道链路上的容量,只要双方的处理能力很强,那么就可以以很大的速率发包,于是链路很快出现拥堵,进而引起大量的丢包,丢包又引发发送端的重传风暴,进一步加剧链路的拥塞。

另外一个拥塞的因素是链路上的转发节点,例如路由器,再好的路由器只要接入网络,总是会拉低网络的总带宽,如果在路由器节点上出现处理瓶颈,那么就很容易出现拥塞。由于 TCP 看不到网络的状况,那么拥塞控制是必须的并且需要采用试探性的方式来控制拥塞,于是拥塞控制要完成两个任务:[1]公平性;[2]拥塞过后的恢复。

TCP 发展到现在,拥塞控制方面的算法很多,其中 Reno 是目前应用最广泛且较为成熟的算法,下面着重介绍一下 Reno 算法(RFC5681)。介绍该算法前,首先介绍一个概念 duplicate acknowledgment(冗余 ACK、重复 ACK)一般情况下一个 ACK 被称为冗余 ACK,要同时满足下面几个条件(对于 SACK,那么根据 SACK 的一些信息来进一步判断)。 [1] 接收 ACK 的那端已经发出了一些还没被 ACK 的数据包; [2] 该 ACK 没有捎带 data; [3] 该 ACK 的 SYN 和 FIN 位都是 off 的,也就是既不是 SYN 包的 ACK 也不是 FIN 包的 ACK; [4] 该 ACK 的确认号等于接收 ACK 那端已经收到的 ACK 的最大确认号; [5] 该 ACK 通知的窗口等接收该 ACK 的那端上一个收到的 ACK 的窗口。

Reno 算法包含 4 个部分:

  • [1]慢热启动算法 – Slow Start;
  • [2]拥塞避免算法 – Congestion Avoidance;
  • [3]快速重传 - Fast Retransimit;
  • [4]快速恢复算法 – Fast Recovery。

TCP 的拥塞控制主要原理依赖于一个拥塞窗口(cwnd)来控制,根据前面的讨论,我们知道有一个接收端通告的接收窗口(rwnd)用于流量控制;加上拥塞控制后,发送端真正的发送窗口=min(rwnd,cwnd)。关于 cwnd 的单位,在 TCP 中是以字节来做单位的,我们假设 TCP 每次传输都是按照 MSS 大小来发送数据,因此你可以认为 cwnd 按照数据包个数来做单位也可以理解,下面如果没有特别说明是字节,那么 cwnd 增加 1 也就是相当于字节数增加 1 个 MSS 大小。

【1】慢热启动算法 – Slow Start

慢启动体现了一个试探的过程,刚接入网络的时候先发包慢点,探测一下网络情况,然后在慢慢提速。不要一上来就拼命发包,这样很容易造成链路的拥堵,出现拥堵了在想到要降速来缓解拥堵这就有点成本高了,毕竟无数的先例告诫我们先污染后治理的成本是很高的。慢启动的算法如下(cwnd 全称 Congestion Window):

  • 1)连接建好的开始先初始化 cwnd = N,表明可以传 N 个 MSS 大小的数据;
  • 2)每当收到一个 ACK,++cwnd; 呈线性上升;
  • 3)每当过了一个 RTT,cwnd = cwnd*2; 呈指数让升;
  • 4)还有一个慢启动门限 ssthresh(slow start threshold),是一个上限,当 cwnd >= ssthresh 时,就会进入"拥塞避免算法 - Congestion Avoidance"。

根据 RFC5681,如果 MSS > 2190 bytes,则 N = 2;如果 MSS < 1095 bytes,则 N =4;如果 2190 bytes >= MSS >= 1095 bytes,则 N = 3;一篇 Google 的论文《An Argument for Increasing TCP’s Initial Congestion Window》建议把 cwnd 初始化成了 10 个 MSS。Linux 3.0 后采用了这篇论文的建议。

【2】拥塞避免算法 – Congestion Avoidance

慢启动的时候说过,cwnd 是指数快速增长的,但是增长是有个门限 ssthresh(一般来说大多数的实现 ssthresh 的值是 65535 字节)的,到达门限后进入拥塞避免阶段。在进入拥塞避免阶段后,cwnd 值变化算法如下:

  • 1)每收到一个 ACK,调整 cwnd 为 (cwnd + 1/cwnd) * MSS 个字节;
  • 2)每经过一个 RTT 的时长,cwnd 增加 1 个 MSS 大小,即cwnd++;

TCP 是看不到网络的整体状况的,那么 TCP 认为网络拥塞的主要依据是它重传了报文段。前面我们说过 TCP 的重传分两种情况:

1)出现 RTO 超时,重传数据包。这种情况下,TCP 就认为出现拥塞的可能性就很大,于是它反应非常’强烈’ [1] 调整门限 ssthresh 的值为当前 cwnd 值的 1/2; [2] reset 自己的 cwnd 值为 1; [3] 然后重新进入慢启动过程。

2)在 RTO 超时前,收到 3 个 duplicate ACK 进行重传数据包。这种情况下,收到 3 个冗余 ACK 后说明确实有中间的分段丢失,然而后面的分段确实到达了接收端,因为这样才会发送冗余 ACK,这一般是路由器故障或者轻度拥塞或者其它不太严重的原因引起的,因此此时拥塞窗口缩小的幅度就不能太大,此时进入快速重传。

【3】快速重传 - Fast Retransimit 做的事情有:

快重传使用了冗余ACK来检测丢包的发生,当发送方连续收到三个重复的ACK报文时,直接重传对方尚未收到的报文段,而不必等待那个报文段设置的重传计时器超时

  • 1) 调整门限 ssthresh 的值为当前 cwnd 值的 1/2;
  • 2) 将 cwnd 值设置为新的 ssthresh 的值;
  • 3) 重新进入拥塞避免阶段。

在快速重传的时候,一般网络只是轻微拥堵,在进入拥塞避免后,cwnd 恢复的比较慢。针对这个,“快速恢复”算法被添加进来,当收到 3 个冗余 ACK 时,TCP 最后的[3]步骤进入的不是拥塞避免阶段,而是快速恢复阶段。

【4】快速恢复算法 – Fast Recovery :

快速恢复的思想是“数据包守恒”原则,即带宽不变的情况下,在网络同一时刻能容纳数据包数量是恒定的。当“老”数据包离开了网络后,就能向网络中发送一个“新”的数据包。既然已经收到了 3 个冗余 ACK,说明有三个数据分段已经到达了接收端,既然三个分段已经离开了网络,那么就是说可以在发送 3 个分段了。于是只要发送方收到一个冗余的 ACK,于是 cwnd 加 1 个 MSS。快速恢复步骤如下(在进入快速恢复前,cwnd 和 sshthresh 已被更新为:sshthresh = cwnd /2,cwnd = sshthresh):

  • 1)把 cwnd 设置为 ssthresh 的值加 3,重传 Duplicated ACKs 指定的数据包;
  • 2)如果再收到 duplicated Acks,那么 cwnd = cwnd +1;
  • 3)如果收到新的 ACK,而非 duplicated Ack,那么将 cwnd 重新设置为【3】中 1)的 sshthresh 的值。然后进入拥塞避免状态。

细心的同学可能会发现快速恢复有个比较明显的缺陷就是:它依赖于 3 个冗余 ACK,并假定很多情况下,3 个冗余的 ACK 只代表丢失一个包。但是 3 个冗余 ACK 也很有可能是丢失了很多个包,快速恢复只是重传了一个包,然后其他丢失的包就只能等待到 RTO 超时了。超时会导致 ssthresh 减半,并且退出了 Fast Recovery 阶段,多个超时会导致 TCP 传输速率呈级数下降。出现这个问题的主要原因是过早退出了 Fast Recovery 阶段。

为解决这个问题,提出了 New Reno 算法,该算法是在没有 SACK 的支持下改进 Fast Recovery 算法(SACK 改变 TCP 的确认机制,把乱序等信息会全部告诉对方,SACK 本身携带的信息就可以使得发送方有足够的信息来知道需要重传哪些包,而不需要重传哪些包),具体改进如下:

  • 1)发送端收到 3 个冗余 ACK 后,重传冗余 ACK 指示可能丢失的那个包 segment1,如果 segment1 的 ACK 通告接收端已经收到发送端的全部已经发出的数据的话,那么就是只丢失一个包,如果没有,那么就是有多个包丢失了;
  • 2)发送端根据 segment1 的 ACK 判断出有多个包丢失,那么发送端继续重传窗口内未被 ACK 的第一个包,直到 sliding window 内发出去的包全被 ACK 了,才真正退出 Fast Recovery 阶段。

我们可以看到,拥塞控制在拥塞避免阶段,cwnd 是加性增加的,在判断出现拥塞的时候采取的是指数递减。为什么要这样做呢?这是出于公平性的原则,拥塞窗口的增加受惠的只是自己,而拥塞窗口减少受益的是大家。这种指数递减的方式实现了公平性,一旦出现丢包,那么立即减半退避,可以给其他新建的连接腾出足够的带宽空间,从而保证整个的公平性。

至此,TCP 的疑难杂症基本介绍完毕了,总的来说 TCP 是一个有连接的、可靠的、带流量控制和拥塞控制的端到端的协议。TCP 的发送端能发多少数据,由发送端的发送窗口决定(当然发送窗口又被接收端的接收窗口、发送端的拥塞窗口限制)的,那么一个 TCP 连接的传输稳定状态应该体现在发送端的发送窗口的稳定状态上,这样的话,TCP 的发送窗口有哪些稳定状态呢?TCP 的发送窗口稳定状态主要有上面三种稳定状态:

参考:https://zhuanlan.zhihu.com/p/199284611 腾讯技术工程「彻底搞懂TCP协议:从 TCP 三次握手四次挥手说起」

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
|
网络协议
用wireshark抓tcp三次握手
用wireshark抓tcp三次握手
65 0
|
5月前
|
网络协议
|
6月前
|
网络协议 架构师 Go
实战TCP三次握手
实战TCP三次握手
36 0
|
网络协议 算法 Linux
TCP 协议-三次握手抓包分析&查看状态
TCP 协议-三次握手抓包分析&查看状态
422 0
|
存储 缓存 网络协议
TCP详解(WireShark抓包分析TCP三次握手和TCP四次挥手)(一)
TCP详解(WireShark抓包分析TCP三次握手和TCP四次挥手)
305 0
|
网络协议 网络性能优化 网络架构
WireShark抓包TCP三次握手和四次挥手
WireShark抓包TCP三次握手和四次挥手
159 0
|
存储 运维 网络协议
网络|学习一下tcp三次握手
网络|学习一下tcp三次握手
140 0
|
网络协议 Linux
tcp的三次握手
tcp的三次握手。
179 0
tcp的三次握手
|
网络协议
TCP的三次握手(2)
TCP的三次握手(2)
112 0
TCP的三次握手(2)
|
网络协议
TCP协议为什么需要三次握手?
TCP实现原理和为什么需要三次握手?两次握手不可以?四次握手不可以?读者可以带着疑问,看一遍本篇博客的详细讲解
185 0
TCP协议为什么需要三次握手?