TCP报头全解-序号确认应答与可靠性是一个准数

TCP 报头全解:序号、确认应答与"可靠性是一个准数"

我的github:(https://github.com/xcx55/ubuntu-linux-project)

感谢各位大佬参观我的github!!

源笔记:TCP底层1/2(26-9-23)、分离的不同:内核和应用层(26-9-23)、TCP底层3(26-9-24)、TCP结构体-序号机制/可靠性本质(26-9-25)、TCP底层4(26-9-26)、缓冲区长这个样子/网络通信辅助图(26-9-26)

UDP 底层看完,轮到 TCP 了。同样是传输层,TCP 报头从 8 字节膨胀到 20 字节起------多出来的每一个字段,都是在为"可靠"付费。这一篇把 TCP 报头逐字段拆开,重点啃两件事:序号机制到底在解决什么 ,以及笔记里最锋利的一句话------可靠性,本质是一个准数。

一、先看报头怎么分离:内核和应用层的分法不一样

复习一下两种分离思路:

  • 应用层协议 (自定义协议、HTTP 等):依据特殊字符 分离数据和报头(\r\n、空行);
  • 内核传输层 :报头本质是结构体 ------依据传输而来的、提前得知的结构体格式 ,按字节切:报头是本层要使用的数据,有效载荷是要传给上层的数据。

TCP 报文按字节一刀切:前 20 字节是定长报头(+选项),剩下全是载荷。有没有取消对齐都能拿到,因为每个字段的位置是写死的。

二、为什么 TCP 没有"总长度"字段,UDP 却有?

UDP 报头里有一个 16 位 UDP 长度(报头+载荷总长度),TCP 却没有------为什么?

UDP 是数据报:发多少,传多少,收多少 ------长度字段有意义;

TCP 是面向字节流:内部自己敲定一次传多少 ,告诉接收方"这一批多少字节"没有意义------反正每一批都要整体交付,上层一次读取得到的到底完不完整,TCP 协议不保证,由上层自己调控。

带上总长度不是不可以,只是多了字段、会被优化掉------历史遗留问题。所以 TCP 的报文长度 = 20 报头 + n 载荷(UDP 是 8 + N)。

还有一个精妙的设计:TCP 的 20 字节不包含选项 。那选项长度怎么表达?------4 位"首部长度"字段,其数值 ×4 = 报头总长 (20+选项)。没有整体长度,只有头长度------剩下的都是载荷,算都算得出来。

三、可靠性的哲学:100% 可靠的协议根本不存在

先做一道逻辑题:A 给 B 发报文,B 要应答;但 B 怎么知道 A 收到了应答?A 要再确认......逻辑递归,无穷无尽。

结论:

  • 互联网里没有 100% 可靠的协议------最新的那条报文,永远无法确认对方收到没有;
  • 但历史报文的应答,是 100% 可靠的------你收到了对一个报文的确认,说明它一定到了。

所以 TCP 的策略是:保证数据报文的可靠性即可------让想要保证可靠性的报文,尽快"变为历史"(发出 → 得到应答 → 历史报文 → 确定送达)。

丢包为什么只能"规定",不能"根除"

丢包有两种情况:1、数据没传到对面;2、返回的应答丢了 。站在发送方视角,这两种情况的表现一模一样------都收不到应答。发送方永远无法区分是"数据丢"还是"应答丢"。

这么多人想不到别的办法吗?不是想不到,是逻辑上无解。所以 TCP 干脆换了个定义:

TCP 不是保证"数据一定被对方收到",而是保证"发送方对一个确定的结果有准数"------收到了应答就继续发下一个;没得到应答,就执行其他策略(重传)。

可靠性 = 准数。这是整套 TCP 机制的地基。

四、32 位序号:一个字段,四个用途

为了配合"准数",序号(seq)登场。它一个字段身兼数职:

  1. 去重 :应答丢了会触发重传,接收方会收到重复报文------32 位序号的核心用途之一就是去重;
  2. 按序排列 :双方并行收发效率高,但并行造成乱序------接收方按序号升序 排回去(这也解释了为什么序号要 32 位:序号必须够多);
  3. 捎带应答的基础 :TCP 双方地位对等(对称),发送和应答的字段是解耦的------任何一方都可能既发数据又捎带应答;
  4. 配合重传优化:见下面的确认序号。

每个字节都有编号

辅助图里的比方很传神:每一个字节,就是一个从 1 开始编号的"躺着的人"。

  • 序号 = 当前报文数据第一个字节的编号;
  • 算个账:报文载荷 3 字节、seq=2000 → 覆盖 2000、2001、2002(最后字节编号 2002)→ 下一个想要的字节编号是 2003------接收方等的就是它。

五、确认序号:一个天才约定

确认序号(ack)= 发出序号 + 1 ,含义是:我想要的下一个字节的序号,从这开始。

它的威力在批量发送时显现:

复制代码
发送方发出:  1000   2000   3000   4000
收到应答:    1001   2001          4001
                              ↑
                    3001 没来 → 3000 那个报文丢了

一个应答缺口,直接定位丢了哪一段 ------发送方不用全部重传,只补缺口。而且这是接收方和发送方共同遵守的约定,返回的确认序号本身就是一种"报文序号规定",有效减少发送方的重发次数。

六、16 位窗口大小:可靠性还有另一半------对面的内存

TCP 缓冲区长这个样子(发送缓冲区/接收缓冲区,都是字节编号的队列)。现在想一个问题:

接收方内存快满了,TCP 发来的数据好不容易到了,操作系统却不要了------是不是效率太低?你只能靠应答让对方反复重传,更慢。

所以设计了流量控制:

  • 接收方衡量自己的接收能力 = 接收缓冲区的剩余空间大小;
  • 怎么告诉发送方?应答报文里的 16 位窗口大小字段;
  • 注意方向:它填写的是发送方视角的对方容量 ------我构建的报文都是发给对方的,所以我必须持续衡量对面 的接收容量,动态调整发送流量;
  • 不能太慢,也不能太快------既保效率,也保准数。

笔记里的感悟值得原样保留:在我看来,可靠性不止体现在报文安全,更体现在对面主机内存安全这另一面! 流量控制,护的是收方的内存。

七、6 个标志位:给报文定型

20 字节报头里还有 6 位标志位,用 bit 位区分报文类型:

  • ACK :应答报文(普通应答 / 捎带应答------捎带是常数级的包传递次数优化,传递报文本身属于 IO,能减少次数最好);
  • SYN :建立连接(三次握手)------TCP 发数据前必须先建连接,因为创建连接是有成本的 :双方 OS 里都要维护 struct tcp_sock 结构体,要为可靠性做大量载入和管理工作;
  • FIN:关闭连接(四次挥手)------断开往往是一方一厢情愿,另一方"不行,我还有数据";
  • RST:和四元组相关,连接出问题时重置(后面细说);
  • PSH :触发中断,在软件层立即唤醒 task_struct 调度运行------让接收进程马上来读缓冲区里已经连接好的数据;
  • URG + 16 位紧急指针 :最少见的一对。URG 是开关,紧急指针本质是偏移量 ------"广义上,具有指向性的东西都可以叫指针"。它指向本次报文载荷中的紧急数据 (带外数据),可以不被按序、优先读取,接收它的函数也不同于 read。

紧急数据为什么存在?为了插队 ------比如"取消上传"这种控制命令,必须优先于普通缓冲区数据抵达对面。但笔记也补了句大实话:一般用得少,建立两个 fd 不就完了?

总结

  • 报头分离:应用层靠特殊字符,内核靠结构体字节布局;TCP 报文 = 20 报头(不含选项)+ n 载荷;
  • UDP 带总长因为它是数据报;TCP 不带因为字节流内部自己定,只有"首部长度×4";
  • 没有 100% 可靠协议 ------最新报文永远无法确认;TCP 保证的是"历史报文可靠":可靠性 = 准数;
  • 丢包无法区分数据丢还是应答丢 → TCP 定义改为"发送方得到确定结果";重传导致重复,seq 去重 ;并行导致乱序,seq 排序 ;序号是第一个字节的编号,确认序号 = 想要的下一个编号,一个缺口定位一段丢失;
  • 16 位窗口 = 流量控制:填的是对面的接收能力,动态调整------可靠性还有"对面内存安全"这一半;
  • 6 标志位:ACK/SYN/FIN/RST/PSH/URG+紧急指针(紧急数据 = 插队的带外数据)。

下一篇:把这些字段用起来------三次握手到底在验证什么、四次挥手为什么要四次,以及 accept/connect 为什么"不参与"握手。

相关推荐
AIgorithmGEEK41 分钟前
[Linux]HTTP 应用层协议全解(中篇)
linux·网络·网络协议·http
ai_xiaogui42 分钟前
PanelAI 1.1.1重磅更新:秒级安装脚本优化 + 无公网IP算力节点组网,私有化AI管理平台全面升级
人工智能·网络协议·tcp/ip·api聚合管理·开发者ai一键部署·ai底层架构解析·ai应用快速变现
91刘仁德1 小时前
HTTPS 加密原理与 CA 数字证书:从对称加密到完整通信流程
网络·笔记·网络协议·http·https
Zelman16 小时前
TCP 协议
网络协议·tcp/ip
life码农18 小时前
Nginx 配置允许指定 IP 段访问:从 192.168.1.1 到 192.168.1.124 及 /24 详解
网络·tcp/ip·nginx
javaDocker18 小时前
16.5 小时,我打掉了两只「SSL 握手失败」的怪
网络·网络协议·ssl
我就是不信19 小时前
TCP 原理详解:从三次握手到拥塞控制
网络·网络协议·tcp/ip
我就是不信20 小时前
TCP/IP 网络编程:从入门到实战
网络·网络协议·tcp/ip
骑着蜗牛撵大象3271 天前
Agent 打字机是怎么来的:SSE 与 WebSocket 打通实时响应与中间状态
网络·websocket·网络协议·agent·sse·实时通信·流式输出