浅谈网络 | 传输层之TCP协议

目录

    • TCP 包头格式
    • TCP 的三次握手
    • TCP 的四次挥手
    • TCP 的可靠性与"靠谱"的哲学
    • TCP流量控制
    • TCP拥塞控制

上一章我们提到,UDP 就像我们小时候一样简单天真,它相信“网之初,性本善,不丢包,不乱序”,因此只提供了传输层必须的最基本字段,例如端口号,用于标识通信的双方。

但随着时间推移,我们逐渐长大,了解了“社会的复杂与残酷”,就像 TCP 协议那样成熟却复杂。相比 UDP 的简单信任,TCP 更加谨慎,它秉承“性恶论”的设计理念,默认认为网络环境是恶劣的,充满了丢包、乱序、重传和拥塞等问题。为了保证数据能够顺利送达,它从算法层面引入了许多机制来保障可靠性。

TCP 包头格式

我们先来看 TCP 头的格式。从这个图上可以看出,它比 UDP 复杂得多。

在这里插入图片描述
相比 UDP 的简单,TCP 包头显得复杂得多。这种复杂性正是为了应对网络的不确定性和可靠性问题。让我们逐步解析 TCP 包头,探讨它背后的设计逻辑和价值。

  1. 端口号:不可或缺的“地址标签”
    源端口号 和 目标端口号 是必不可少的字段,与 UDP 一样,用来标识数据的来源和去向。
    如果没有端口号,数据就像一封没有写收件人地址的信,完全无法送达应用程序。
    这是传输层与应用层沟通的基础。

  2. 包的序号:解决乱序的“稳重派”
    序号 是 TCP 的关键字段,作用是给数据包编号,用于解决乱序问题。
    TCP 是“社会老司机”,面对数据乱序,它会一件一件地处理,确保所有数据稳重有序地到达接收方。
    为什么需要序号?
    如果没有编号,接收方就无法判断哪个包应该先来,哪个应该后到。通过序号,TCP 能将“错乱的数据”还原为有序的字节流。

  3. 确认序号:保证可靠的“承诺派”
    确认序号 是 TCP 的另一大特点,主要用于实现可靠性。
    每个发送的数据包都需要收到确认。若未收到确认,TCP 会重传,直到对方确实收到为止。
    为什么需要确认?
    IP 层不保证可靠性,包可能会丢失,而 TCP 通过“重传机制”尽力弥补丢包的不足。
    这种设计体现了 TCP 的“靠谱”:承诺一定送达,即便网络环境差,也会通过努力完成任务。

  4. 状态位:维护连接的“社交礼仪”
    TCP 的状态位是它实现“面向连接”特性的核心。
    例如:

     SYN:发起连接时使用,表示“你好,我要建立连接了”。ACK:确认收到数据包,表示“好的,我知道了”。FIN:结束连接时使用,表示“再见,我们的通信结束了”。RST:异常情况时重置连接。
    

    这些状态位就像“社交礼仪”:

     初次见面时礼貌寒暄(SYN),交谈时互相回应(ACK),离开时正式告别(FIN)。
    

人与人之间的信任需要经过多次交互建立,TCP 连接的维护也同样如此。

  1. 窗口大小:懂分寸的“流量控制器”

TCP 的 窗口大小 是实现流量控制的重要字段。
通信双方通过窗口字段声明各自当前的处理能力,以避免以下两种情况:

  • 发得太快,接收方处理不过来(“撑死”)。
  • 发得太慢,浪费带宽资源(“饿死”)。

窗口控制就像老司机的“分寸感”:既能合理提出要求,又不强人所难,确保通信双方的平衡。

  1. 拥塞控制:知进退的“自我控制”
    TCP 的拥塞控制则更进一步:
    当网络环境变差(丢包、延迟增加)时,TCP 会主动降低发送速度,避免加剧网络拥堵。

为什么需要拥塞控制?

  • 如果网络通路已经堵塞,再大量发包只会导致更多丢包和更高延迟。
  • TCP 的拥塞控制机制让它学会“自我克制”,以保护整个网络的稳定性。

这是 TCP 的“成熟之道”:无法改变外部环境时,就通过改变自己适应环境。

重点解析 TCP 协议的五大问题
通过解析 TCP 包头,我们可以总结其核心设计思路:

  1. 顺序问题:稳重不乱

通过序号字段,确保数据按序到达。

  1. 丢包问题:承诺靠谱

通过确认序号和重传机制,保证数据可靠送达。

  1. 连接维护:有始有终

借助状态位(如 SYN、FIN),实现完整的连接管理。

  1. 流量控制:把握分寸

使用窗口大小,调节发送速率,避免“撑死”或“饿死”。

  1. 拥塞控制:知进知退

通过算法调整发送行为,适应网络状况,保护通信效率。

TCP 的三次握手

在网络通信中,TCP 的连接建立被称为 三次握手,是确保通信可靠性和序号同步的核心过程。这个过程看似简单,但背后蕴含了丰富的逻辑与机制。以下我们以“请求 -> 应答 -> 应答之应答”的形式逐步解析三次握手的原理、必要性及细节优化。

为什么需要三次握手?
为什么不能两次握手? 在一个不可靠的网络环境中,客户端 A 发送建立连接的请求(SYN 包),但可能发生以下情况:

请求丢失:网络传输中,SYN 包可能未抵达服务器 B。
绕路延迟:即使 SYN 包没有丢失,也可能在网络中绕路或超时。
无响应:目标服务器 B 因忙碌或配置原因未响应。
因此,A 无法直接判断连接是否建立,需要继续重发 SYN 包。假设某次请求最终到达 B,B 知道了 A 的连接请求,若 B 不愿意建立连接,A 的重试最终超时,连接失败即可。如果 B 同意连接,则 B 会回复 A 一个 SYN+ACK 包。然而:

仅两次握手存在风险:如果 B 在发送 SYN+ACK 后,A 未响应(例如 ACK 包丢失),B 将误以为连接已成功,导致连接状态的不一致。甚至,旧的 SYN 包重传还可能诱发无效连接。

因此,两次握手不足以确保双方均能正确确认连接状态。

为什么不采用四次或更多握手? 虽然增加握手次数可以提高可靠性,但网络的天然不确定性(例如丢包、延迟等)决定了握手次数过多也无法完全避免问题。事实上,TCP 设计的目标是在合理的开销内确保双方消息有去有回即可。一旦双方完成三次握手:
A 确认了 B 收到自己的 SYN 并愿意建立连接。
B 确认了 A 收到自己的 SYN+ACK 并发送了 ACK。
从此时起,双方可以安全地通信。

三次握手的过程
以下是 TCP 三次握手的具体流程及每步状态变化:

第一步:A -> B(SYN)
客户端 A 发送 SYN 包,表明希望建立连接,并处于 SYN-SENT 状态。

第二步:B -> A(SYN+ACK)
服务端 B 收到 SYN 包后,同意连接请求,同时发送自己的 SYN 包并确认 A 的 SYN(即 ACK)。此时,B 进入 SYN-RCVD 状态。

第三步:A -> B(ACK)
客户端 A 收到 B 的 SYN+ACK 后,回复 ACK,确认连接成功。此时,A 进入 ESTABLISHED 状态,同时 B 收到 ACK 后也进入 ESTABLISHED 状态,连接正式建立。

序号同步:三次握手的关键任务
三次握手不仅用于建立连接,还用于同步双方的 TCP 包序号。每个连接需要独立的序号以避免数据混淆:

  • A 告诉 B:起始序号从 X 开始。
  • B 告诉 A:起始序号从 Y 开始。

为什么序号不能从固定值(如 1)开始?
假设序号固定,可能发生以下问题:

A 与 B 建立连接并发送数据 {1,2,3}。因网络问题,包 3 延迟到达。
A 重新连接 B,序号再次从 1 开始,发送数据 {2,4}。此时延迟到达的旧包 3 会被误认为是当前连接的数据,导致错误。
为避免冲突,TCP 序号采用动态生成,通常以系统启动时间为基准,每 4ms 增加 1。32 位序号的重复周期接近 4 小时,而 IP 包的 TTL(生存时间)通常不足此时长,因此不会因序号重复导致数据混淆。

连接建立后的优化
在三次握手完成后,A 和 B 的通信即将开始,但仍需考虑以下问题:

  1. 后续数据传输解决丢包问题

    如果三次握手中的最后一个 ACK 包丢失,B 不会立即关闭连接。A 后续发送的数据包会间接确认连接已建立。
    如果 A 长时间不发送数据,B 可通过 KeepAlive 机制 定期发送探测包,检测连接状态。

  2. 空闲连接的资源释放

    如果客户端 A 在建立连接后未发送数据,B 可以主动关闭长时间空闲的连接,以释放资源给其他客户端。

三次握手与状态机
三次握手对应的 TCP 状态变化如下:

  1. 初始状态:

    客户端:CLOSED → 发送 SYN 后进入 SYN-SENT。
    服务端:监听端口,处于 LISTEN 状态。

  2. 握手过程:

    服务端收到 SYN 后,进入 SYN-RCVD,并回复 SYN+ACK。
    客户端收到 SYN+ACK 后,进入 ESTABLISHED,并回复 ACK。
    服务端收到 ACK 后,也进入 ESTABLISHED。
    至此,双方均完成状态确认,开始正常通信。
    在这里插入图片描述

TCP 的四次挥手

在 TCP 协议中,连接的关闭过程被称为 四次挥手。这个过程确保双方优雅断开连接,既支持双方数据的完整传输,又能避免因旧连接导致的新问题。以下以对话形式详细解析四次挥手及异常处理机制。
四次挥手的过程
四次挥手的过程对应客户端 A 和服务端 B 的状态转换,是连接终止的核心步骤:

第一步:A -> B(FIN)
A 表示自己不再发送数据,向 B 发送 FIN 包,进入 FIN_WAIT_1 状态。此时,A 可能还有一些数据需要接收。

第二步:B -> A(ACK)
B 收到 A 的 FIN 包后,回复一个 ACK 表示确认,但 B 可能还有未完成的任务,因此进入 CLOSE_WAIT 状态。此时,A 收到 ACK 后进入 FIN_WAIT_2 状态,等待 B 的 FIN 包。

第三步:B -> A(FIN)
当 B 完成数据传输后,向 A 发送 FIN 包,进入 LAST_ACK 状态,等待 A 的最终确认。

第四步:A -> B(ACK)
A 收到 B 的 FIN 包后,发送 ACK 确认连接关闭,并进入 TIME_WAIT 状态,等待一段时间以确保 B 收到 ACK。B 收到 ACK 后进入 CLOSED 状态,至此连接完全关闭。A 等待超时后也进入 CLOSED 状态。
在这里插入图片描述

四次挥手的设计原理
为什么需要四次挥手? 与连接建立的三次握手类似,关闭连接的四次挥手也是为了避免状态不同步问题。TCP 是全双工协议,允许双方独立关闭各自的发送通道:

  • A 发送 FIN,表示不再发送数据,但仍然可以接收数据。
  • B 在回复 ACK 后,可能仍有数据需要发送,因此不能立即关闭连接。

四次挥手的设计确保双方能独立完成数据的发送与接收,同时避免意外断开连接导致数据丢失。

为什么需要 TIME_WAIT 状态?

  • 可靠性保障:A 在发送最后的 ACK 后,可能会因网络问题导致 B 未收到 ACK。B 会重传 FIN,A 需要处于 TIME_WAIT
    状态以重新发送 ACK 确保 B 正确关闭连接。
  • 防止端口复用冲突:TIME_WAIT 确保旧连接的所有数据包(包括延迟的或重复的)在网络中失效后,端口才可以重新分配,避免干扰新连接。

四次挥手的状态转换详解
四次挥手的状态转换如下:

  1. A 发送 FIN,进入 FIN_WAIT_1 状态:A 主动发起关闭。
  2. B 收到 FIN,进入 CLOSE_WAIT 状态:B 确认 A 的 FIN,但继续处理自己的事务。
  3. B 发送 FIN,进入 LAST_ACK 状态:B 表示自己也完成数据传输。
  4. A 收到 FIN,发送 ACK,进入 TIME_WAIT 状态:A 确认连接关闭,等待 2MSL 时间。
  5. B 收到 ACK,进入 CLOSED 状态:B 完全关闭连接。A 等待超时后进入 CLOSED 状态。

异常情况处理
A 发送 FIN 后直接关闭
如果 A 发送 FIN 后直接断开,B 无法确认连接是否已关闭,可能重试发送数据或 FIN 包,导致连接不一致。解决方法:

  • A 等待 TIME_WAIT 时间:在 TIME_WAIT 期间,A 可响应 B 的重传 FIN,并重新发送 ACK 确保连接关闭。

B 未发送 FIN 或超时未收到 ACK
如果 B 在发送 FIN 后长时间未收到 A 的 ACK,B 会重传 FIN。但超过 2MSL 后,B 若仍未收到 ACK,会放弃等待并发送 RST 包强制关闭连接。

端口复用问题
如果 A 在 TIME_WAIT 状态期间立即重新使用同一端口,新应用可能收到旧连接中的延迟数据包。通过 TIME_WAIT,确保旧连接的数据包失效后再分配端口。

MSL(Maximum Segment Lifetime)的作用
MSL 是 TCP 报文在网络中生存的最大时间,通常用于确定 TIME_WAIT 的等待时间:

  • 确保 ACK 能被成功接收:TIME_WAIT 持续 2 倍 MSL,足够时间覆盖 FIN 重传及 ACK 的传递。
  • 避免端口复用导致的数据混淆:等待 2MSL 后,旧连接中的所有数据包都已失效,端口可安全复用。

实际应用中的 MSL:

  • 协议标准规定 MSL 为 2 分钟,但实际实现中常设置为 30 秒或 1 分钟以加快端口回收。

TCP 状态机
状态机简介
TCP 状态机包含两大核心流程:

  1. 连接建立(握手):通过三次握手完成通信通道的初始化,确保双方对连接的状态和序号达成一致。

  2. 连接断开(挥手):通过四次挥手终止通信,保障数据完整传输,并防止连接复用冲突。

加粗的实线表示客户端 A 的状态变迁,加粗的虚线表示服务端 B 的状态变迁。时序以 阿拉伯数字(连接) 和 大写中文数字(断开) 表示,清晰标记了各步骤的顺序。

在这里插入图片描述

连接建立

  1. CLOSED → LISTEN(服务端启动监听) 服务端 B 启动监听端口,进入 LISTEN 状态,等待客户端连接。
  2. CLOSED → SYN_SENT(客户端发起连接) 客户端 A 主动发起连接,发送 SYN 包,进入 SYN_SENT 状态。
  3. LISTEN → SYN_RCVD(服务端收到 SYN) 服务端 B 收到 SYN 包,发送 SYN+ACK,同时进入 SYN_RCVD
    状态。
  4. SYN_SENT → ESTABLISHED(客户端收到 SYN+ACK,回复 ACK) 客户端 A 收到 SYN+ACK 后,发送
    ACK,进入 ESTABLISHED 状态。
  5. SYN_RCVD → ESTABLISHED(服务端收到 ACK) 服务端 B 收到客户端的 ACK 后,进入 ESTABLISHED
    状态,连接建立完成。

连接断开
四次挥手的核心流程:

(一)ESTABLISHED → FIN_WAIT_1(客户端主动断开)
客户端 A 发送 FIN 包,进入 FIN_WAIT_1 状态,表明不再发送数据。

(二)ESTABLISHED → CLOSE_WAIT(服务端确认 FIN)
服务端 B 收到 FIN 包,发送 ACK 确认后进入 CLOSE_WAIT 状态。

(三)FIN_WAIT_1 → FIN_WAIT_2(客户端确认 ACK)
客户端 A 收到 ACK 后,进入 FIN_WAIT_2 状态,等待服务端的 FIN。

(四)CLOSE_WAIT → LAST_ACK(服务端主动断开)
服务端 B 完成最后的数据传输后,发送 FIN 包,进入 LAST_ACK 状态。

(五)FIN_WAIT_2 → TIME_WAIT(客户端确认 FIN)
客户端 A 收到服务端的 FIN 包后,发送 ACK 确认,进入 TIME_WAIT 状态。

(六)LAST_ACK → CLOSED(服务端关闭)
服务端 B 收到 ACK 后,进入 CLOSED 状态,连接完全关闭。

(七)TIME_WAIT → CLOSED(客户端关闭)
客户端 A 在等待 2MSL 后,确保所有残留包失效,关闭连接。

TCP 的可靠性与"靠谱"的哲学

TCP 协议以其严谨的设计和稳健的可靠性,成为互联网通信的基石。如果将 TCP 比喻成一个想要成为 “靠谱” 的人的话,那么它的每一个机制都蕴含着深刻的人生智慧。以下从现实职场管理和 TCP 的传输原理出发,优化并阐释 TCP 是如何成为一个“靠谱的人”。

玄奘出网关:公网中的挑战
玄奘西行,离开国境进入陌生的土地,需要面对未知的风险和挑战。同样,TCP 在公网传输中也需要面对网络的 不可靠性:

  1. 数据可能丢失:网络拥堵导致的数据包丢失。
  2. 数据可能延迟:数据绕远路或者等待资源。
  3. 数据可能重复:网络中的残留包导致重复接收。

为了在这种环境下做到 可靠传输,TCP 必须具备恒心(坚持重试)和智慧(巧妙的算法设计)。

"靠谱"的定义:TCP 的可靠传输哲学
如何成为一个靠谱的人?TCP 用自己的方式给出了答案:

  • 及时响应:任务交代后,必须有应答,不让事情“石沉大海”。
  • 主动重试:对未完成的任务持续跟进,直到明确结果。
  • 有序管理:任务有明确的编号和顺序,防止遗漏和混乱。
  • 高效协作:支持多任务并行,不浪费彼此时间。

TCP 的核心机制正是基于这些理念设计的。以下我们结合职场场景和 TCP 的工作原理,具体展开。

发送端和接收端的缓存管理及流量控制

发送端的缓存管理
发送端需要维护缓存以跟踪数据包的状态,缓存划分为四个部分:

  1. 已发送且确认的部分(可靠完成)

    这些是发送端交代的任务,并已经收到接收端的确认,标志着任务完成,可从缓存中移除。
    数据结构:LastByteAcked 记录这一部分的最后一个字节。

  2. 已发送但未确认的部分(待确认任务)

    已发送给接收端,但尚未收到确认。TCP 会等待确认,如果超时,则重新发送。
    数据结构:LastByteAcked 和 LastByteSent 之间的部分。

  3. 未发送但可发送的部分(计划任务)

    这些任务尚未分配,但可随时发送。受限于接收端的 AdvertisedWindow,只有接收端表示可以接受时,才能发送。
    数据结构:LastByteSent 和 LastByteAcked + AdvertisedWindow 之间的部分。

  4. 未发送且暂不可发送的部分(未来任务)

    超出了接收端的处理能力,暂时无法发送的任务。
    数据结构:LastByteAcked + AdvertisedWindow 之后的部分。

关键数据结构:

LastByteAcked:表示已确认部分的最后一个字节。
LastByteSent:表示已发送但未确认部分的最后一个字节。
AdvertisedWindow:接收端通告的可接收窗口,决定发送端可发送数据的范围。

在这里插入图片描述

接收端的缓存管理
接收端的缓存相对简单,但仍需要维护以下三部分:

  1. 已接收且确认的部分

    接收端已成功接收并确认的数据,可供应用层使用。
    数据结构:LastByteRead 记录这一部分的最后一个字节。

  2. 未接收但可接收的部分

    接收端能够接收的最大数据量,表示其最大工作能力。
    数据结构:由 NextByteExpected 和 MaxRcvBuffer 决定。

  3. 未接收且不可接收的部分

    超出接收端缓存能力的部分,接收端无法处理。

关键数据结构:

LastByteRead:应用层已读取的最后一个字节。
NextByteExpected:下一个期望接收的字节,用于标识数据包的顺序。
MaxRcvBuffer:接收端的总缓存容量。

窗口大小计算:

AdvertisedWindow = MaxRcvBuffer - ((NextByteExpected - 1) - LastByteRead)
NextByteExpected + AdvertisedWindow 定义了可接收窗口的边界。

在这里插入图片描述
流量控制与流畅协作
TCP 的流量控制机制类似于项目管理中的“把握分寸”原则,通过接收端的 AdvertisedWindow 限制发送端的任务量,避免资源过载:

  • 动态调整窗口大小:接收端根据自己的处理能力动态调整

  • AdvertisedWindow,通知发送端实时更新。

  • 空闲与过载防控:

    窗口过小:发送端过于保守,导致接收端空闲。
    窗口过大:接收端超载,可能导致数据丢失。

通过窗口的动态调整,TCP 实现了任务分配的“适量性”,既不让资源闲置,也不超负荷。

有序性与累计确认
TCP 的可靠性不仅体现在数据的传输成功,还体现在数据的顺序正确性:

  1. 序列号:每个数据包都带有唯一的序列号,发送端和接收端通过序列号确保数据按顺序处理。
  2. 累计确认:接收端发送的 ACK 表示所有小于当前序列号的数据包均已收到,未确认的部分将被重传。
  3. 乱序处理:即使数据包乱序到达,接收端仍能根据序列号重新排序,确保数据连续性。

累计确认的优势:

  • 减少 ACK 数量,提高效率。
  • 允许部分乱序,提高容错能力。

顺序问题与丢包问题的总结
TCP 协议在不可靠的网络环境中,通过精细的设计解决了数据包顺序问题与丢包问题。以下从确认与重传机制的核心逻辑出发,结合具体例子,总结 TCP 的应对策略及优化设计。

顺序问题与丢包问题的例子
假设发送端和接收端的状态如下:

发送端:

1、2、3:已发送并确认,无问题。
4、5、6、7、8、9:已发送但未确认。
10、11、12:未发送但可发送。
13、14、15:接收端没有空间,不可发送。

接收端:

1、2、3、4、5:已接收并确认,但尚未被应用层读取。
6、7:未接收但可接收。
8、9:已接收但未确认(因为前面的 6、7 尚未到达)。

问题分析:

顺序问题:发送端的包 6、7 丢失,8、9 虽然到达,但因数据乱序,接收端无法确认。
丢包问题:包 5 的 ACK 丢失,导致发送端以为包 5 未被接收,可能会触发不必要的重传。

确认与重传机制
TCP 为解决顺序问题与丢包问题,设计了多种重传机制,其中包含超时重传、快速重传和选择性确认(SACK)。

1. 超时重传
当发送端发送的数据包未收到 ACK,TCP 会通过超时定时器触发重传。以下是其核心逻辑:

  • 设定定时器:对每个未确认的数据包,启动定时器。

  • 超时重传:如果超过预设时间未收到 ACK,重新发送该数据包。

  • 自适应超时(RTT 估算):

    TCP 通过采样 RTT(往返时间),动态调整超时时间。
    超时时间公式:

    EstimatedRTT=(1−α)×EstimatedRTT+α×SampleRTT
    TimeoutInterval=EstimatedRTT+4×DevRTT
    

    其中 DevRTT 表示 RTT 的波动范围,用于适配网络环境的变化。

指数退避(Exponential Backoff):

  • 当网络状况恶劣时(连续超时),每次超时时间间隔加倍,避免频繁重传导致拥堵。

优点:适应网络延迟变化,确保丢失的数据最终被重传。
缺点:超时触发的周期可能较长,影响传输效率。

2. 快速重传
为了缩短等待超时的时间,TCP 提供了快速重传机制。核心逻辑:

  1. 冗余 ACK:

    当接收端检测到乱序数据包时,发现数据中断(如丢失包 7),会重复发送 ACK。
    例如,接收端收到包 8 和 9,未收到包 7,则会连续发送三个 “ACK6”,表明期望收到的是包 7。

  2. 触发重传:

    当发送端收到 3 个相同的冗余 ACK 后,认为对应的数据包丢失,立即重传该包,而无需等待超时。

示例:

  • 接收端收到 6、8、9,但未收到 7,则发送三个 ACK6。
  • 发送端收到 3 个冗余 ACK6,立即重传包 7。

优点:显著缩短丢包后的恢复时间,提高网络传输效率。
缺点:只适用于乱序或轻度丢包的情况,不能标识多个不连续丢失的数据包。

TCP流量控制

流量控制机制,在对于包的确认中,同时会携带一个窗口的大小。
假设窗口不变的情况,窗口始终为 9。4 的确认来的时候,会右移一个,这个时候第 13 个包也可以发送了。
在这里插入图片描述
这个时候,假设发送端发送过猛,会将第三部分的 10、11、12、13 全部发送完毕,之后就停止发送了,未发送可发送部分为 0。
在这里插入图片描述
当对于包 5 的确认到达的时候,在客户端相当于窗口再滑动了一格,这个时候,才可以有更多的包可以发送了,例如第 14 个包才可以发送。
在这里插入图片描述
如果接收方实在处理的太慢,导致缓存中没有空间了,可以通过确认信息修改窗口的大小,甚至可以设置为 0,则发送方将暂时停止发送。

我们假设一个极端情况,接收端的应用一直不读取缓存中的数据,当数据包 6 确认后,窗口大小就不能再是 9 了,就要缩小一个变为 8。
在这里插入图片描述
这个新的窗口 8 通过 6 的确认消息到达发送端的时候,你会发现窗口没有平行右移,而是仅仅左面的边右移了,窗口的大小从 9 改成了 8。
在这里插入图片描述
如果接收端还是一直不处理数据,则随着确认的包越来越多,窗口越来越小,直到为 0。

在这里插入图片描述
当这个窗口通过包 14 的确认到达发送端的时候,发送端的窗口也调整为 0,停止发送。
在这里插入图片描述
如果这样的话,发送方会定时发送窗口探测数据包,看是否有机会调整窗口的大小。当接收方比较慢的时候,要防止低能窗口综合征,别空出一个字节来就赶快告诉发送方,然后马上又填满了,可以当窗口太小的时候,不更新窗口,直到达到一定大小,或者缓冲区一半为空,才更新窗口。

这就是我们常说的流量控制。

TCP拥塞控制

TCP 的拥塞控制通过 拥塞窗口(cwnd) 和 接收窗口(rwnd) 协同工作,确保网络利用率的最大化,同时避免拥塞的发生。
基本概念:滑动窗口与拥塞窗口

  • 滑动窗口(rwnd): 防止接收端缓存溢出,由接收端通告的可接收窗口大小。
  • 拥塞窗口(cwnd): 防止网络拥堵,由发送端动态调整。
  • 核心公式:
    LastByteSent−LastByteAcked≤min(cwnd,rwnd)
    
    发送方已发送但未确认的字节数必须小于两个窗口中较小的那个。

TCP 如何判断网络是否满?
TCP 对网络的状态一无所知,只能通过以下反馈间接推测:

  • ACK 的返回速度: 如果 ACK 返回缓慢,可能说明网络传输延迟增加。

  • 丢包或超时: 网络设备的缓存溢出会导致数据丢失。

  • 发送速率与接收速率的平衡: 当发送速度大于网络或接收端处理能力时,会导致拥塞。

TCP 类比为往水管中灌水:

  • 水管粗细(带宽): 每秒能传输的最大数据量。

  • 水管长度(延迟): 数据从一端到另一端的往返时间(RTT)。

  • 通道容量:

    通道容量=带宽×RTT
    

在这里插入图片描述
如图所示,假设往返时间为 8s,去 4s,回 4s,每秒发送一个包,每个包 1024byte。已经过去了 8s,则 8 个包都发出去了,其中前 4 个包已经到达接收端,但是 ACK 还没有返回,不能算发送成功。5-8 后四个包还在路上,还没被接收。这个时候,整个管道正好撑满,在发送端,已发送未确认的为 8 个包,正好等于带宽,也即每秒发送 1 个包,乘以来回时间 8s。

拥塞控制的核心机制

  1. 慢启动(Slow Start)

    工作原理:

    初始设置:cwnd = 1。
    每次收到一个 ACK,cwnd 增加 1,形成指数性增长:
    第 1 轮:cwnd = 1,发送 1 个包;
    第 2 轮:cwnd = 2,发送 2 个包;
    第 3 轮:cwnd = 4,发送 4 个包;
    第 4 轮:cwnd = 8,发送 8 个包。
    指数增长直到达到慢启动阈值(ssthresh)。
    优点:

    能够快速填满通道,适应较大的网络带宽。
    结束条件:

    cwnd 达到 ssthresh。
    检测到拥塞(例如包丢失)。

  2. 拥塞避免(Congestion Avoidance)

    进入条件:
    cwnd 超过 ssthresh。
    增长方式:
    从指数增长转为线性增长:
    每次收到 ACK,cwnd 增加 1/cwnd
    假设 cwnd = 8,收到 8 个 ACK,则 cwnd 增加 1。
    下次 cwnd = 9。
    目的:
    缓慢提升窗口,避免网络过载。

  3. 拥塞检测与恢复
    快速重传算法
    超时重传:
    触发条件: 数据包超时未收到 ACK。
    处理策略:
    ssthresh = cwnd / 2。
    cwnd = 1,重新进入慢启动。
    缺点:
    重置为 1 的策略过于保守,导致传输速率骤降。
    快速恢复(Fast Recovery):
    触发条件: 接收端发送 3 个冗余 ACK(快速重传机制)。

    处理策略:

    cwnd 减半:cwnd = cwnd / 2。
    ssthresh 设置为当前 cwnd。
    从 cwnd = ssthresh 开始线性增长,而不是重新慢启动。
    优点:
    避免“一夜回到解放前”,更适合高吞吐量网络。

TCP 拥塞控制的综合过程

  1. 阶段 1:慢启动

    cwnd 从 1 开始指数增长。
    达到 ssthresh 或检测到拥塞时结束。

  2. 阶段 2:拥塞避免

    cwnd 开始线性增长。
    若检测到拥塞:
    超时重传: cwnd 重置为 1,重新慢启动。
    快速恢复: cwnd 减半,从 ssthresh 开始线性增长。

  3. 阶段 3:拥塞检测

    包丢失或超时:
    轻微拥塞(快速重传): 减小 cwnd,但保持较高速率。
    严重拥塞(超时重传): cwnd 重置为 1,重新慢启动。

示例应用
假设:
RTT = 8s,带宽 = 1 个包/s,每个包 1024 字节。
通道容量 = 8 个包。
过程:

  1. 第 1 秒:

    发送包 1,cwnd = 1。

  2. 第 2 秒:

    收到包 1 的 ACK,cwnd = 2,发送包 2 和 3。

  3. 第 3 秒:

    收到包 2 和 3 的 ACK,cwnd = 4,发送包 4、5、6、7。

  4. 第 4 秒:

    cwnd 达到 8,通道满载。

  5. 拥塞发生:

    包丢失或超时触发快速恢复或超时重传。

TCP 的拥塞控制是动态且自适应的,通过 慢启动 和 拥塞避免 保持传输效率,通过 快速恢复 降低丢包的冲击。这种机制保证了 TCP 在复杂网络条件下的可靠性与高效性,同时最大程度地利用了网络资源

TCP 的问题与 BBR 拥塞控制算法优化总结

TCP 的传统拥塞控制机制旨在避免两个主要问题:丢包 和 缓存填满导致的超时重传。然而,传统 TCP 拥塞控制也存在以下局限性:
第一个问题是丢包并不代表着通道满了,也可能是管子本来就漏水。例如公网上带宽不满也会丢包,这个时候就认为拥塞了,退缩了,其实是不对的。

第二个问题是 TCP 的拥塞控制要等到将中间设备都填充满了,才发生丢包,从而降低速度,这时候已经晚了。其实 TCP 只要填满管道就可以了,不应该接着填,直到连缓存也填满。

为了优化这两个问题,后来有了TCP BBR 拥塞算法。它企图找到一个平衡点,就是通过不断的加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/478509.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB相关问题

视频教程 【GeekHour】20分钟掌握MongoDB Complete MongoDB Tutorial by Net Ninja MongoDB开机后调用缓慢的原因及解决方法 问题分析: MongoDB开机后调用缓慢,通常是由于以下原因导致: 索引重建: MongoDB在启动时会重建索引…

嵌入式驱动开发详解3(pinctrl和gpio子系统)

文章目录 前言pinctrl子系统pin引脚配置pinctrl驱动详解 gpio子系统gpio属性配置gpio子系统驱动gpio子系统API函数与gpio子系统相关的of函数 pinctrl和gpio子系统的使用设备树配置驱动层部分用户层部分 前言 如果不用pinctrl和gpio子系统的话,我们开发驱动时需要先…

[模版总结] - 树的基本算法4 -最近公共祖先 LCA

什么是最近公共祖先LCA LCA:在一个树中,距离两个节点p,q最近可以是其本身并且同时包含这两个子节点的节点 题目连接 Leetcode 236 - LCA Leetcode 1644 - LCA II Leetcode 1650 - LCAIII Leetcode 1123 - LCA of Deepest leaves 基本思路 Leetcode 23…

永磁同步电机末端振动抑制(输入整形)

文章目录 1、前言2、双惯量系统3、输入整形3.1 ZV整形器3.2 ZVD整形器3.3 EI整形器 4、伺服系统位置环控制模型5、仿真5.1 快速性分析5.2 鲁棒性分析 参考 1、前言 什么是振动抑制?对于一个需要精确定位的系统,比如机械臂、塔吊、码头集装箱等&#xff…

jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js

参考资料: jQuery-Word-Export导出word_jquery.wordexport.js下载-CSDN博客 近期又需要自己做个 Html2Doc 的解决方案,因为客户又不想要 Html2pdf 的下载了,当初还给我费尽心思解决Html转pdf时中文输出的问题(html转pdf文件下载之…

【Redis_Day6】Hash类型

【Redis_Day6】Hash类型 Hash类型操作hash的命令hset:设置hash中指定的字段(field)的值(value)hsetnx:想hash中添加字段并设置值hget:获取hash中指定字段的值hexists:判断hash中是否…

【CSP CCF记录】201809-2第14次认证 买菜

题目 样例输入 4 1 3 5 6 9 13 14 15 2 4 5 7 10 11 13 14 样例输出 3 思路 易错点:仅考虑所给样例,会误以为H和W两人的装车时间是一一对应的,那么提交结果的运行错误就会让你瞬间清醒。 本题关键是认识到H和W的装车时间不一定一一对应&…

Unity清除所有的PlayerPrefs

方法1: 直接在你的代码中加入这句 PlayerPrefs.DeleteAll(); 方法2: 点击编辑窗口的这里

非交换几何与黎曼ζ函数:数学中的一场革命性对话

非交换几何与黎曼ζ函数:数学中的一场革命性对话 非交换几何(Noncommutative Geometry, NCG)是数学的一个分支领域,它将经典的几何概念扩展到非交换代数的框架中。非交换代数是一种结合代数,其中乘积不是交换性的&…

微信小程序下拉刷新与上拉触底的全面教程

微信小程序下拉刷新与上拉触底的全面教程 引言 在微信小程序的开发中,用户体验至关重要。下拉刷新和上拉触底是提高用户交互体验的重要功能,能够让用户轻松获取最新数据和内容。本文将详细介绍这两个功能的实现方式,结合实际案例、代码示例和图片展示,帮助开发者轻松掌握…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例:⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

论文阅读:A fast, scalable and versatile tool for analysis of single-cell omics data

Zhang, K., Zemke, N.R., Armand, E.J. et al. A fast, scalable and versatile tool for analysis of single-cell omics data. Nat Methods 21, 217–227 (2024). 论文地址:https://doi.org/10.1038/s41592-023-02139-9 代码地址:https://github.com…

Hive离线数仓结构分析

Hive离线数仓结构 首先,在数据源部分,包括源业务库、用户日志、爬虫数据和系统日志,这些都是数据的源头。这些数据通过Sqoop、DataX或 Flume 工具进行提取和导入操作。这些工具负责将不同来源的数据传输到基于 Hive 的离线数据仓库中。 在离线…

设计模式之 模板方法模式

模板方法模式是行为型设计模式的一种。它定义了一个算法的骨架,并将某些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。 模板方法模式的核心在于: 封装算法的骨架:通过父类中的模板方…

【分治】--- 快速选择算法

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: 算法Journey 🏠 颜色划分 📌 题目解析 颜色分类 本题要求我们原地对元数组划分0,1,2三个区域,也就是不能使用辅助数组&#xf…

万物皆可Docker,在NAS上一键部署最新苹果MacOS 15系统

万物皆可Docker,在NAS上一键部署最新苹果MacOS 15系统 哈喽小伙伴们还,我是Stark-C~ 最近苹果Mac mini 2024款在政府补贴的加持下,仅需3500块钱左右就能到手确实挺香的。我看很多评论区的小伙伴跃跃欲试,但是也有不少之前从未体…

C++设计模式行为模式———状态模式

文章目录 一、引言二、状态模式三、总结三、总结 一、引言 状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。其实现可完成类似有限状态机的功能。换句话说,一个对象可以处…

vscode自动打印日志插件

自动日志工具(Auto Logger Log) 概述 自动日志工具(Auto Logger Log) 是一款 VS Code 扩展,用于简化生成调试日志的过程。它可以为选中的变量自动生成打印语句,帮助开发者快速记录和调试代码。该扩展支持多…

优雅的不等式——Hard

上一文《Easy》末尾出现了又要我们证明的例子,Hard难度就是继续答题答下去 其实一样可以用那篇文章https://zhuanlan.zhihu.com/p/669285539中的式子继续算下去,但是有三个系数,实在是太费时间和人力了 翻到下面的第十九种类型,可…

虚拟局域网PPTP配置与验证(二)

虚拟局域网PPTP配置与验证(二) windows VPN客户端linux 客户端openwrt客户端性能验证虚拟局域网PPTP配置与验证(一)虚拟局域网PPTP配置与验证(二) : 本文介绍几种客户端连接PPTP服务端的方法,同时对linux/windows/openwrt 操作系统及x86、arm硬件平台下PPTP包转发性能进…