:序号、确认应答与流量控制机制详解)
小叶-duck个人主页❄️个人专栏《Data-Structure-Learning》《C入门到进阶自我学习过程记录》《Linux系统从入门到实践》《Linux网络从入门到实践》《Qt 方寸极境》 《MySQL》✨未择之路不须回头已择之路纵是荆棘遍野亦作花海遨游目录前言一、TCP 核心知识点回顾二、 TCP 发送数据的两种工作模式2.1 串行发送模式停等协议 Stop-and-Wait2.2 并行发送模式流水线模式 Pipelining三、TCP 序号与确认序号机制3.1 序号的本质3.2 确认序号定义与累积确认机制3.3 序号的三大核心功能3.4 缓冲区与序号的逻辑关系3.5 经典场景中间报文丢包的处理3.5.1 前置基础定义3.5.2 正常无丢包场景的序号流转3.5.3 报文 3 丢失、报文 4 乱序提前到达场景3.5.4 补齐缺失报文后的确认应答3.5.5 核心结论3.6 问题为什么同时需要序号和确认序号3.6.1 全双工通信要求双方都能发送和确认3.6.2 捎带应答一个报文同时承载数据和 ACK3.6.3 区分捎带应答纯ACK应答3.6.4 小结四、TCP 16 位窗口大小与流量控制4.1 流量控制产生背景4.2 接收方接收能力的量化标准4.3 16 位窗口大小字段含义4.4 流量控制的本质提高效率4.5 窗口探测机制零窗口死锁规避4.5.1 窗口探测视角理解TCP面向字节流4.6 核心总结结束语前言在上一篇文章中我们深度解析了 TCP 报头中 4 位首部长度字段的设计精髓以及可靠性最底层的两大基石 —— 确认应答与超时重传机制。但 TCP 的复杂之处远不止于此为了提高传输效率TCP 不会像停等协议那样发一个等一个而是支持并行发送多个报文面对多个报文同时发送带来的丢包、乱序、重复等问题TCP 依靠序号与确认序号这套字节级编号体系来化解为了避免发送方发送过快导致接收方缓冲区溢出TCP 引入了流量控制机制并借助 16 位窗口大小字段实时同步双方的接收能力。本文将继续沿着 TCP 协议的设计思路往下走先从数据发送的两种工作模式讲起厘清停等协议与流水线模式的取舍再深度拆解序号与确认序号的核心作用还原丢包、乱序场景下 TCP 的真实处理逻辑随后详解流量控制与窗口探测机制。所有内容均严格基于 TCP 协议规范和 Linux 内核实现力求做到理论与实践相结合。一、TCP 核心知识点回顾在开始本篇文章新内容之前我们先梳理上篇文章中TCP的基础核心结论作为后续内容理解的前提报文交互规则TCP 通信双方交互的基本单元是完整 TCP 报文。发送方传输业务数据时数据会封装成 TCP 报文接收方返回的应答报文就算不携带任何应用层数据也必须保留完整 TCP 报头不会单独只发送标志位。确认应答核心规则TCP 属于全双工可靠传输协议确认应答是实现可靠性的基础。应答报文由接收端操作系统内核的 TCP 协议栈自动生成并回复不需要应用层参与。 通信规则A 向 B 发送 TCP 数据B 收到后自动返回 ACK 应答应答报文本身不再需要二次应答否则会形成无限循环严重降低传输效率。依托这套机制通信双向都可以保障数据传输可靠。超时重传判定逻辑发送方收到 ACK 应答就可以 100% 确认接收方完整收到数据本次传输可靠。 如果发送方没有收到应答无法区分是原始数据丢包还是 ACK 应答报文在路上丢失。TCP 统一处理超时时间内没有收到对应应答就判定传输异常自动触发报文重传。TCP 可靠性的真正定义TCP 的可靠不等于强制保证数据一定送达接收方。可靠性的核心本质是无论传输最终成功还是失败发送方一定能够感知最终结果。收到 ACK 代表数据交付成功超时无应答则判定传输失败并重传。就算遇到网线断开、网络中断这类场景TCP 也可以精准感知传输失败的状态。全双工特性TCP 是全双工协议通信两端能够同时收发数据。这个特性深刻影响 TCP 的诸多设计捎带应答、三次握手等机制都建立在全双工的基础之上。二、 TCP 发送数据的两种工作模式TCP 为适配不同的数据传输场景设计了两套发送工作模式串行发送与并行流水线发送模式。2.1 串行发送模式停等协议 Stop-and-Wait工作逻辑发送方每发出 1 个报文之后就暂停发送原地等待接收方返回对应的 ACK 应答。必须收到应答才可以继续发送下一份报文。优点逻辑简单天然不会出现报文乱序、重复接收的问题实现成本低。缺点传输效率很低。在等待 ACK 的 RTT 往返时间内网络链路处于空闲状态带宽资源被浪费。适用场景只适合少量数据传输场景在真实 TCP 通信里很少单独使用。主机A 主机B |---- 数据1 ------| | | |---- ACK1 -------| | | |---- 数据2 ------| | | |---- ACK2 -------|2.2 并行发送模式流水线模式 Pipelining这是 TCP 实际通信里主流默认使用的发送模式。工作逻辑发送方不需要等待上一个报文的 ACK 应答就可以连续向外发送多个报文不需要等待一轮 RTT。多个报文的收发时间可以相互重叠充分利用链路带宽大幅提升网络吞吐与传输效率。主机A 主机B |------ 数据1 ------| |------ 数据2 ------| |------ 数据3 ------| |----- ACK1 -------| |----- ACK2 -------| |------ 数据4 ------| |----- ACK3 -------|并行流水线模式虽然解决了性能瓶颈但同时引入了新难题如果多个报文连续发出中间某个报文丢失发送方如何定位到底是哪一段数据丢包报文在网络路由转发时乱序到达接收端怎么还原出原始数据流顺序超时重传带来重复报文接收端如何识别并丢弃重复数据为了解决并行流水线带来的丢包识别、报文排序、去重等一系列问题TCP 协议在报头中定义了两个核心字段32 位序号Sequence Number和32 位确认序号Acknowledgment Number。三、TCP 序号与确认序号机制序号与确认序号是 TCP 实现可靠传输与流水线高效传输的核心TCP 绝大多数可靠性机制都建立在这两个字段之上。3.1 序号的本质操作系统内核维护发送缓冲区与接收缓冲区底层依靠sk_buff存储报文。逻辑层面TCP 把缓冲区中的数据流视为一个巨大连续字节数组序号就是这个字节数组的下标。TCP 不会逐字节发送数据而是将数据分批封装成 TCP 数据段Segment进行传输。举例报文承载第 11000 字节数据该报文的序号 1下一段承载 10012000 字节序号 1001再下一段承载 20013000 字节序号 20013.2 确认序号定义与累积确认机制确认序号计算公式确认序号 收到的最后一个完整字节的序号 1含义确认序号 N代表 N 之前所有字节全部接收完毕下一次期望从序号 N 开始接收数据。示例主机 B 收到 1~1000 字节的数据段回复确认序号 1001主机 B 收到 1001~2000 字节的数据段回复确认序号 2001。TCP 采用累积确认这是非常关键的特性 如果主机 B 已经收到 1~1000、2001~3000但中间 1001~2000 还未收到此时只能回复确认序号 1001。 哪怕后面的字节提前到达也不会确认后面的数据以此保障 TCP 有序交付。累积确认优势容错能力强中间部分 ACK 报文在网络丢失也不会造成严重影响只要后续 ACK 抵达发送方就能确认数据。3.3 序号的三大核心功能正是依靠序号和确认序号流水线并行发送带来的各类难题得以解决保障传输可靠性接收方通过确认序号反馈接收进度发送方能精准判断哪些数据成功送达、哪些报文丢失触发超时重传。报文去重超时重传场景下接收端可能收到重复报文依靠序号识别重复数据并丢弃。实现乱序重组网络传输时报文可能乱序抵达接收缓冲区利用序号重新排序整理成有序数据流再交付应用层。3.4 缓冲区与序号的逻辑关系发送缓冲区、接收缓冲区在内核中以 sk_buff 队列物理存储。逻辑上等效为连续字节数组缓冲区里的每一字节数据都对应唯一序号下标。 TCP 数据段发送时按字节范围打包。例如一段报文包含 1000 字节起始序号 1覆盖字节 1~1000下一段就从 1001 开始。不同数据段的序号自然不连续根源是每个 TCP 段携带的数据长度不一样。3.5 经典场景中间报文丢包的处理场景发送方依次发送序号 100、200、300、400 四段报文序号 300 的报文在传输途中丢失400 报文先抵达接收端。此时接收方不能直接应答 400仍然持续回复确认序号 300。即便收到 400 这一段接收端也会告知发送方300 之前的数据已经全部收到请从 300 继续发送。只有 300 号报文补齐之后确认序号才会继续向后推进。面试小结确认序号只确认连续的前置字节后面提前到达的数据不会单独确认这就是累积确认。3.5.1 前置基础定义TCP 是面向字节流的协议全部数据都会按字节分配连续序号序号对应内核接收缓冲区的字节下标。报文起始序号该报文携带的第一个字节的下标报文结束序号该报文携带的最后一个字节的下标 起始序号 报文长度 - 1确认序号 ACK接收方返回期望收到的下一个字节下标含义我已经完整收到 ACK 序号之前的全部字节这就是 TCP 累积确认的核心规则。3.5.2 正常无丢包场景的序号流转连续发送 4 个报文每个报文固定承载 100 字节数据报文编号起始序号携带字节范围结束序号下一个报文起始序号报文 111~100100101报文 2101101~200200201报文 3201201~300300301报文 4301301~400400401网络正常无丢包、不乱序时接收方收到报文后回复对应的确认序号收到报文 1 → 返回 ACK1011~100 字节已全部接收下次从 101 开始发送收到报文 2 → 返回 ACK2011~200 字节已全部接收下次从 201 开始发送收到报文 3 → 返回 ACK3011~300 字节已全部接收下次从 301 开始发送收到报文 4 → 返回 ACK4011~400 字节已全部接收下次从 401 开始发送3.5.3 报文 3 丢失、报文 4 乱序提前到达场景发送方按顺序发送报文 1 → 报文 2 → 报文 3 → 报文 4。 网络传输发生异常报文 1、报文 2 正常抵达接收端报文 3201~300 字节在传输途中丢失报文 4301~400 字节绕过路由先于报文 3 到达接收方。接收方缓冲区当前状态✅ 连续完整收到1~200 字节报文 1、报文 2✅ 提前零散收到301~400 字节报文 4临时存放在接收缓冲区❌ 缺失空缺段201~300 字节报文 3重点说明 虽然报文 4 已经提前到达并存入接收缓冲区接收方依然只能回复 ACK201不能返回 ACK401。累积确认有硬性约束确认序号只能标记已经连续收到的最大字节的下一位不能跳过中间缺失的字节。如果返回 ACK401会误导发送方让发送方认为 1~400 全部收到不会重传丢失的 201~300 这一段最终造成数据永久丢失。接收端只会暂存提前抵达的报文 4但不会更新确认序号必须等空缺的 201~300 字节补齐之后确认序号才会向后推进。 悬念此时发送方收到 ACK201 重复应答如何判断是报文 3 发生丢失、需要重传报文 3该部分依赖滑动窗口、快速重传机制我们留到后面章节详细讲解。3.5.4 补齐缺失报文后的确认应答发送方触发重传重新发送报文 3201~300 字节。接收端收到重传的报文 3 后缓冲区 1~400 字节全部补齐此时接收方返回 ACK401。ACK401 属于累积确认它的含义是1~400 的所有字节我都完整收到。中间的 ACK201、ACK301 应答报文就算在网络中丢失也没关系只要发送方收到最高确认序号 ACK401就代表前置全部字节送达不需要重复重传任何报文直接从 401 序号继续传输后续数据3.5.5 核心结论TCP 序号是字节级编号不是报文编号报文之间序号不连续是因为每个报文承载多个字节。确认序号永远等于已连续接收的最后一个字节序号 1。乱序提前到达的报文会被接收缓冲区暂存但确认序号不会向前推进直到空缺字节补齐(重点理解)。累积确认自带容错能力中间 ACK 应答丢失不影响传输只要收到最高序号的确认就代表前面所有数据全部接收成功。3.6 问题为什么同时需要序号和确认序号在前面的例子里我们看到确认序号可以表示 “我已经收到了哪些字节”。于是有人会问既然确认序号已经能说明接收进度为什么不直接只用一个32位序号字段应答时把序号加 1 再添到32位序号中返回不就行了这个问题的关键在于TCP 是全双工协议通信双方可以同时发送数据。在这种情况下发送方不仅要标识 “自己发的数据到了哪里”还要标识 “自己确认收到了对方哪些数据”。3.6.1 全双工通信要求双方都能发送和确认TCP 允许两端同时传输数据。主机 A 可以向主机 B 发送数据主机 B 也可以同时向主机 A 发送数据。这意味着每一端都需要一个 “发送序号”用来标记自己发送数据的字节位置每一端也需要一个 “确认序号”用来告诉对方我已经收到了你的哪些数据。如果只有一个序号字段就无法同时区分当前报文是在描述 “我发送到哪里”还是在描述 “我确认收到了哪里”。因此序号和确认序号是两个独立职责字段作用序号标识本报文携带的数据在发送方字节流中的位置确认序号标识接收方已经连续收到的最大字节位置3.6.2 捎带应答一个报文同时承载数据和 ACKTCP 的全双工特性带来了一个重要优化捎带应答。当主机 B 需要向主机 A 发送数据时它不必单独发送一条空的 ACK 报文而是可以把对主机 A 的确认信息直接放到自己的业务数据报文中一起发送。例如主机 A 先向主机 B 发送数据字节范围是 11000主机 B 收到后本来可以返回一个 ACK1001但如果此时主机 B 也有数据要发给主机 A比如字节范围是 50016000那么主机 B 可以直接发送一个同时包含数据的报文自己的发送序号5001对主机 A 的确认序号1001。这样这个报文既携带了主机 B 的业务数据又完成了对主机 A 的确认减少了单独 ACK 报文的数量提高了传输效率。3.6.3 区分捎带应答纯ACK应答引出问题3.6.4 小结TCP 之所以同时需要序号和确认序号根本原因有两个TCP 是全双工协议两端可以同时发送数据必须分别跟踪各自的发送进度TCP 支持捎带应答一个报文可以同时承载业务数据和确认信息因此需要同时标识 “我发的数据” 和 “我确认的数据”。这也进一步说明序号和确认序号并不是两个孤立的数字而是 TCP 可靠性、顺序控制和流量控制的重要基础。后续讲解滑动窗口时还会看到这两个字段如何配合窗口大小共同决定发送方可以连续发送多少数据。四、TCP 16 位窗口大小与流量控制4.1 流量控制产生背景即便网络带宽充足发送方也不能无限制持续发送数据。接收主机的处理能力存在上限如果发送速率过快内核接收缓冲区会被迅速填满后续到达的数据会直接丢弃。数据包一旦丢失会触发 TCP 超时重传反复重传会浪费网络带宽、主机 CPU 以及内存资源传输效率大幅下降。TCP 引入流量控制解决这个问题而 TCP 头部的16 位窗口大小字段就是流量控制的核心载体。4.2 接收方接收能力的量化标准接收方的接收能力由内核接收缓冲区的剩余空闲空间决定接收缓冲区的剩余空间越大代表接收能力越强可以接收更多数据接收缓冲区的剩余空间越小接收能力越弱接收缓冲区的剩余空间为 0缓冲区已满无法接收新数据。4.3 16 位窗口大小字段含义窗口大小字段表示接收方当前接收缓冲区剩余空闲字节数量单位是字节。 接收方在回复 ACK 应答报文时会把自身接收缓冲区剩余空间写入窗口大小字段告诉发送方当前还能接收多少字节的数据。 发送方读取 ACK 中的窗口值动态调整发送速度窗口大接收方处理压力小发送方可以持续发送较多数据窗口小接收方缓冲区剩余空间紧张发送方需要降低发送速率窗口为 0接收缓冲区已满发送方必须暂停发送业务数据。计算公式窗口大小 接收缓冲区总大小 - 缓冲区已使用字节数4.4 流量控制的本质提高效率4.5 窗口探测机制零窗口死锁规避当接收方窗口等于 0发送方停止发送业务数据。 这里存在一个风险接收方后续窗口更新的 ACK 报文如果在网络传输中丢失发送方会一直阻塞等待双方陷入死锁。TCP 引入窗口探测机制解决该问题 发送方收到窗口为 0 的 ACK 后会周期性发送仅携带 1 字节数据的窗口探测报文用来询问接收方当前最新窗口大小。如果接收方窗口已经恢复返回携带新窗口值的 ACK发送方恢复数据传输如果窗口依旧为 0接收方回复窗口 0发送方继续等待下一次周期再探测。4.5.1 窗口探测视角理解TCP面向字节流4.6 核心总结流量控制解决发送方和接收方主机处理能力不匹配的问题和网络拥塞不是一回事拥塞控制后续讲解16 位窗口大小由接收方填充反映接收缓冲区剩余空间用于告知发送方接收上限窗口为 0 时发送方暂停发送依靠窗口探测报文防止窗口更新报文丢失带来的死锁问题流量控制全程动态交互让发送速率跟随接收方处理能力自适应变化减少丢包与不必要的重传。结束语回顾本篇我们承接上一篇文章对确认应答与超时重传的讲解把视角从 数据怎么保证送达 推进到 数据怎么传得又快又稳。我们先是理清了停等协议与流水线两种发送模式的区别明白 TCP 之所以默认采用并行发送是为了消除等待应答的往返空档充分榨取链路带宽。随之而来的丢包定位、乱序重组、重复报文识别则依靠序号与确认序号这套字节级编号体系解决。通过丢包场景的推演我们看到累积确认如何保证有序交付也理解了为什么序号和确认序号两个字段缺一不可。最后流量控制机制借助 16 位窗口字段动态调节发送速率解决了接收方缓冲区溢出的隐患窗口探测则化解了零窗口下的死锁风险。值得注意的是流量控制针对的是收发两端处理能力不匹配的问题与后续要讲的拥塞控制并非同一概念。序号、窗口、重传这些机制环环相扣共同撑起 TCP 复杂而严谨的传输控制体系也为下一部分学习三次握手、四次挥手与连接管理打下了坚实基础。