抓包

Wireshark / tshark · 按 tick 对齐 · 返回总条目

在客户端与本地服务端之间抓一次,按 50 ms 的 tick 对齐,就能直接看到 C08 与半个 C07 是否落在同一批帧里、服务端有没有下发 S32,以及每个包的 ID 与长度前缀。

两个前提

不满足这两条,抓到的字节流解不开:

抓环回流量要选 loopback 适配器(Windows 上是 Npcap 的 Npcap Loopback Adapter),不要选物理网卡。

抓一次

  1. 起本地服务端,客户端连 127.0.0.1:25565。
  2. 开始抓包,过滤器先留空,等进入游戏后再加条件。
  3. 主手持弓,拉满 20 tick,按流程发出这一串。
  4. 停止抓包,套用显示过滤器 tcp.port == 25565 && tcp.len > 0。

命令行版本,按时间顺序导出每个 TCP 段的时间戳与载荷:

tshark -r pullbow.pcapng -Y "tcp.port == 25565 && tcp.len > 0" \
  -T fields -e frame.number -e frame.time_relative -e tcp.len -e tcp.payload

要看整条连接的重组字节流,用 follow:

tshark -r pullbow.pcapng -q -z follow,tcp,raw,0

怎么切包

TCP 是字节流,一个段里可能塞了好几个包,也可能把一个包拆到两个段里。所以不要按段数包,要按字节流切:

  1. 读第一个 VarInt,得到这一帧的长度。
  2. 往后取该长度的字节,得到载荷。
  3. 载荷的第一个 VarInt 就是包 ID。
  4. 按该 ID 对应的字段结构把剩下的字节读完,继续下一帧。

VarInt 每字节 7 位有效数据,最高位为 1 表示还有后续字节。包 ID 是 VarInt 而不是定长整数,这也是手工切包最容易错的一步。

怎么判断同一 tick

服务端主循环 20 tick/秒,一 tick 50 ms。同一 tick 内发出的几个包,抓包时间戳通常相差不到 1 ms;跨 tick 的则至少差 40 ms 以上。用 frame.time_relative 排序看差值就够分辨。

严格判定要回到客户端侧:在发包处打时间戳并与主循环帧号对齐,再和抓包时间戳比对。抓包只能给出网络上的先后,给不出客户端内部的执行顺序。

看到的现象说明下一步
C08 与 C07 落在同一毫秒这一轮对齐成立继续看服务端有没有回 S32
两者相差 40 ms 以上跨了 tick,C08 的重置没有作用到释放上检查发送路径是否经过主线程队列
只有 C08,没有 C07释放没有发出去,或被压缩阈值改变了解析先确认压缩状态,再核对切包
有 S32 但没有 C0F回号没走异步路径检查 Pong 是否被排队
一串 C04 连续出现级联上报,位移与朝向都在里面对照 Serverbound 一侧的字段表

常见坑

切出包之后,把 ID 对照一下:1.21.4 的取值见包 ID 对照;字段含义见Clientbound 与Serverbound 两侧。