抓包文件里有一个确认号覆盖了请求的全部字节,能否据此认定服务端已经处理请求?不能。这个确认号属于 TCP 的接收状态;业务处理发生在应用读取、解析和执行之后。要回答“请求停在哪里”,必须把报文记录与端点日志放到同一条有边界的证据链上。

第 02 篇已经定义四字节长度头和有界消息体。本篇沿用该格式,只发送合成字符串 network-03,观察应用调用、连接端点与序号之间的关系。实际环境完成了回环回显和序号手算,但抓包设备权限不足,未取得真实 pcap;下文把已运行的部分、可复跑的捕获步骤和未验证结论分别标出。

报文记录首先属于一个捕获点

应用调用 sendall,数据进入传输实现;抓包程序在操作系统提供的捕获接口观察记录;接收应用再调用 recv,取得可读字节。这些事件并不是同一个事件的不同显示方式。一个观察点缺少另一个观察点的信息,不能靠增加输出字段补齐。

1
2
3
客户端调用日志 → 本机 TCP 路径 → 回环捕获点 → 本机 TCP 路径 → 服务端调用日志

pcap 文件及分析器显示

图中只表示观察关系,不承诺所有系统内部函数的调用顺序。回环交换不经过物理以太网链路,因此本篇不验证网卡发送、交换机转发或路由器行为。对物理接口的捕获,驱动、卸载和捕获钩子的位置又会影响所见记录。

完整说明一次捕获,至少需要接口、过滤表达式、开始与结束条件、保存长度和工具版本。只提供一张有红色提示的截图,无法知道握手是否早于捕获开始、是否抓错接口、是否截短了包,也无法知道筛选条件隐藏了哪些报文。

抓包文件还有自己的链路类型。不能因为讨论的是 TCP,就固定跳过十四字节“以太网头”后解析 IP。官方定义的 LINKTYPE_NULL 使用四字节主机字节序协议族头,后面才是载荷;实际文件采用什么格式应从文件元数据读取,不能把某个平台的回环格式泛化到所有系统。

过滤减少范围,也限制结论

捕获过滤器在收集阶段决定哪些记录进入文件。分析器的显示过滤器则从已有记录中选择展示内容。两者发生在不同阶段:显示过滤可以撤销,捕获阶段没有保存的字节不能事后恢复。

本篇只允许观察自建服务。先让脚本绑定 127.0.0.1 的临时端口,再把日志中的端口填入过滤表达式,既缩小噪声,也避免收集其他服务流量。监听端口由运行中的 socket 保持占用,不通过“寻找空端口后关闭它”的方式留下竞争窗口。

1
PYTHONDONTWRITEBYTECODE=1 python3 source/_posts/2026-09-19-计算机网络03-抓包能证明什么/experiment.py --wait-for-capture

脚本打印 listener_ready 后等待回车。在另一个终端读取该次端口,设置 PORT,再启动捕获。以下是 macOS lo0 接口的命令模板;其他系统必须使用已核实的回环接口名。

1
2
PORT=填入本次listener_ready中的端口
sudo tcpdump -i lo0 -n -s 0 -w /tmp/network-03.pcap "tcp port ${PORT} and host 127.0.0.1"

只有具备相应权限的自建实验环境才执行此命令。本次没有执行 sudo、安装权限辅助程序或改变捕获设备权限。捕获程序报告开始监听后,回到脚本终端按回车;交换完成后在捕获终端按 Ctrl-C,保留退出计数,再读取文件。

1
2
tcpdump -n -r /tmp/network-03.pcap
tcpdump -n -S -r /tmp/network-03.pcap

-i 选择接口,-w 写原始捕获文件,-r 读取文件,-S 显示绝对 TCP 序号。这里用上游文档明确记载的 -n 禁止地址和端口等名字转换,不把不同版本的 -n-nn 用法差异写成普适规则。

当次核查的上游 tcpdump 4.99.5 手册规定,-s 0 恢复默认 262144 字节捕获长度。它不是无限长度,也不能据此保证任何记录都不会截短。本机工具报告 tcpdump 4.99.1、Apple 161、libpcap 1.10.1;上游手册与本机版本分开记录,选项语义和实际截断状态仍需结合本机手册与文件检查。

用字节范围关联日志

附件脚本复用第 02 篇的 framing.py,先建立本机 TCP 连接,记录客户端与服务端的地址、端口,然后发送一条消息。脚本是有限单次交换,故意先完成客户端小消息写入,再调用服务端读取;这给“发送返回时应用尚未读取”提供明确的程序顺序。

network-03 是十个 ASCII 字节,加四字节长度头后,提交给 TCP 的数据共十四字节。编码为:

1
00 00 00 0a 6e 65 74 77 6f 72 6b 2d 30 33

服务端每次最多读取五字节,解析器跨读取保留状态,得到完整十字节消息后原样回显。客户端再次解析并比较消息。读取日志里的块大小是应用观察,不是分段方式;不能根据三次 recv 推断线上必有三个 TCP 段。

关联真实捕获时,先用地址、端口和方向确定连接,再在每个方向累计数据字节范围。应用帧的四字节头同样属于 TCP 数据,不能只数十字节业务字符串。发生重传时,相同序号范围可能重复出现,也不能把所有包长直接相加当作应用新收到的数据量。

时间只用于辅助定位。脚本同时记录单调时钟和墙上时钟:单调时钟适合同一进程内比较事件间隔;捕获工具的时间戳应先核对时钟来源与精度,不能把两个不同来源的数值直接相减。日志记录发生在调用前后,它也不是内核处理的精确瞬间。

本次脚本运行获得 sendall_returnserver_message_decodedclient_reply_verified,最后记录本地交换完成。附件保留原始日志。它证明本机端点最终解析出相同消息,不证明抓包链路完整,也不证明数据何时被 TCP 确认。

相对序号和确认号的含义

Wireshark 默认相对显示 TCP 序号和确认号,便于阅读长整数。显示为零的 SYN,不表示线上序号字段真的为零;它只是分析器选择了相对起点。中途开始捕获时,显示起点也不能证明已经观察到连接建立。

RFC 9293 §3.4按字节组织序号空间。每个数据字节占一个序号;SYN 和 FIN 各自也占一个序号,纯 ACK 不占。序号运算按模 2^32 进行。为了先排除回绕影响,设一个教学连接的初始序号是 1000。

教学发送事件 SEQ 数据字节 控制位占用 连续范围之后的下一序号
SYN 1000 0 1 1001
本篇完整帧 1001 14 0 1015
纯 ACK 1015 0 0 1015
FIN 1015 0 1 1016

这张表是手算输入,不是本次抓包结果。附件运行了对应断言,并额外检查 2^32 - 2 后连续四字节的下一序号为 2。它验证算术和教学约定,未验证任何内核发送的实际字段。

累计确认号 X 表示接收端下一期待的序号,确认了 X 之前连续收到的序号范围。对上表连续、无缺口的数据而言,确认号 1015 覆盖十四字节帧;但不能进一步推出服务端已经调用 recv、通过长度校验或执行业务。

公式 下一序号 = SEQ + 数据字节数 + SYN占用 + FIN占用 描述该连续范围的右边界,不保证每个报文都会立即触发一个等于此值的 ACK。存在缺口、重复数据、延迟确认等条件时,观察到的确认行为需要额外状态。把所有数据段统一加一,会把 SYN/FIN 的特殊占用错误地加到普通数据上。

分析器提示不是独立的故障结论

Wireshark 的 TCP 分析依赖捕获文件中已经出现的记录及其顺序。例如“Previous segment not captured”的判断使用当前序号与下一预期序号的关系。这个提示首先表明当前记录序列缺少某个范围;缺失可能发生在网络,也可能发生在捕获机制、过滤过程或开始时间之前。

tcpdump 的退出计数同样要按定义读。captured 是程序接收处理的数量;received by filter 的具体含义依赖操作系统;dropped by kernel 反映抓包机制因缓冲等原因丢弃的记录,不能直接当作网络丢包量。操作系统未报告这类信息时,零也不能证明捕获完整。

发送方向出现校验和异常提示,还要考虑校验和卸载:捕获可能发生在网卡补齐校验和之前。分段或聚合卸载也可能让捕获记录大于通常预期的链路 MTU。因而一个大记录不自动对应链路上的一个等长帧,一个异常校验和也不自动证明线上损坏。

本次没有物理网卡捕获,也没有修改卸载开关,以上属于官方文档支持的解释条件。若要证明某次故障,需要记录捕获位置,并与另一端证据或受控对照关联;不能看到“可能是卸载”就反向断言卸载一定是原因。

运行结果与未完成的捕获验收

脚本只使用 Python 标准库,但依赖本系列第 02 篇的帧解析模块。在仓库中可直接运行;单独下载时,把 framing.py 与脚本放在同一目录,也能按 Python 模块搜索规则导入。

1
PYTHONDONTWRITEBYTECODE=1 python3 source/_posts/2026-09-19-计算机网络03-抓包能证明什么/experiment.py

本次 Python 3.14.4、Darwin 27.0.0 arm64 环境中的回显和手算断言通过。捕获权限探测则先建立独占的回环监听端口,再使用 dumpcap 对该端口执行一秒捕获,结果退出码为 1,报告无法打开 /dev/bpf0。原始错误见附件,没有补造 pcap 文件或把其他流量当作本次实验。

因此,计划要求的“调用日志与报文对齐”只完成端点侧准备,包级对齐仍待有捕获权限的隔离环境执行。补证时应保存原始 pcap、退出计数、端点日志和工具版本,核对十四字节请求及对应回复;不能只保存一张显示确认号的截图就把验收标记为完成。

附件:实验脚本本次端点日志与手算结果捕获权限原始错误。帧模块位于第 02 篇的附件中。

练习

序号练习。 假设 SYN 的绝对序号为 9000,之后连续发送二十字节普通数据,再发送 FIN。分别给出数据起点、数据后的下一序号和 FIN 后的下一序号。

核对:数据从 9001 开始,数据后的下一序号为 9021,FIN 后为 9022。若二十字节拆成两个十字节段,第二段从 9011 开始;拆段不增加应用数据的序号占用。

证据练习。 抓包可见覆盖完整请求的累计 ACK,但应用日志没有“解析完成”。列出两种仍然可能的状态,以及一种能区分它们的新增观察。

核对:应用尚未读取,或者已读取但解析失败,都与这个 ACK 相容。增加接收返回和解析结果日志可以区分这两种状态;只有发送端抓包不能直接回答服务端业务是否提交。

一手资料

以下资料于 2026-09-19 写作当次核查。上游版本说明与本机实测分别使用,不用文档代替尚未成功的抓包。