测试版本为 1.20.6,插件服务器,理论上适用于 1.7 + 版本服务器,已知 forge 服务器在测试延迟时会出现未收到 Server response 的情况

开源 Minecraft Server Status 已在 Github 发布,项目地址:https://github.com/GoodBoyboy666/Minecraft-Server-Status

# 数据包结构

在 wiki.vg 上我们可以看到,Minecraft 数据包的格式如下图:

图片

整个数据包格式采用 数据包长度 + Packet ID + 数据 的格式。

数据包长度Packet ID 均使用 VarInt 数据类型,具体可以看 varint 是啥你真的知道么?

这里的 数据包长度 仅包含 Packet ID + 数据 的长度,不包含 数据包长度 自身的长度。

# 握手

TCP 三次握手不再赘述,这里主要讲与 Minecraft Server 之间的握手

图片

从上图中可以看出 HandShake 包的大致结构: Packet ID + Protocol Version + Server Address + Server Port + Next state

HandShake 包的 ID 为 0x00Protocol Version 具体可以参考 Protocol version numbersServer AddressServer Port 则为服务器地址和端口, Next state 为下一步状态, 1 为获取状态, 2 为登录。

因此参考 wiki 后 理论上 整个完整的 HandShake 包结构为:

VarInt类型的Pakect Length + Packet ID + VarInt类型的Protocol Version + String类型的Server Address + Unsigned Short类型的Server Port + VarInt类型的Next state

但实际上通过抓包后发现 Protocol VersionServer Address 仍存在字节,经过测试后发现为 Server Address 的 VarInt 类型的长度数值,因此正确完整的 HandShake 包结构为:

VarInt类型的Pakect Length + Packet ID + VarInt类型的Protocol Version + VarInt类型的Server Address Length + String类型的Server Address + Unsigned Short类型的Server Port + VarInt类型的Next state

受大小端影响, Server Port 部分转换为 byte 内容后顺序可能会受影响,但不影响服务器响应,实测 MC 客户端以大端排序和模拟客户端以小端排序发送包后均可以得到服务器正确响应。

例如一次抓包得到客户端 HandShake 十六进制数据为: 18 00 fe 05 11 6d 63 2e 67 6f 6f 64 62 6f 79 62 6f 79 2e 74 6f 70 63 dd 01

拆分后可以得到:

18 为 VarInt 类型的整个数据包长度(不包含 18 )——24 字节

00 为 Packet ID——0x00

fe 05 为 VarInt 类型的 Protocol Version——766

11 为 VarInt 类型的 Server Address 的长度 ——17 字节

6d 63 2e 67 6f 6f 64 62 6f 79 62 6f 79 2e 74 6f 70 为 Server Address——mc.goodboyboy.top

63 dd 为 Unsigned Short 类型的 Server Port——25565(大端排序)

01 为 VarInt 类型的 Next state——0x01

# 获取状态

通过下图可以看到 Status Request 包的结构:

图片

因此整个完整的 Status Request 包为:

VarInt类型的Pakect Length + Packet ID

无需带任何数据

通过抓包可以得到 01 00

01 为整个包长度(不包含 01 )——1 个字节

00 为 Packet ID,请求服务器状态。

# 状态响应

从下图可以看出主要的数据载体为 JSON

图片

服务器状态响应包结构在 wiki 上没有很直观展示,只对返回的 json 进行了展示,经过测试后得出下面结构:

VarInt类型的Pakect Length + Packet ID + VarInt类型的JSON数据长度 + JSON数据

将 JSON 数据解析出来即可得到服务器状态 Motd

# Ping

在获取完服务器状态后可以直接进行 Ping 测试,测试精度取决于当前计算机以及实现代码。

从下图中可以看出 Ping 包的结构:

图片

结构同样是:

VarInt类型的Pakect Length + Packet ID + 数据

因为只是进行延迟测试,所以数据具体内容不重要,但不能没有。

抓包数据: 09 01 00 00 00 00 00 00 2e 5d

通过抓包发现客户端的 Ping 包包含了 8 个字节数据,加上 1 位 Pakect Length 与 Packet ID,总数据包大小为 10 字节。

推荐使用客户端格式使用 8 位任意数据。

在发送 Ping 包后服务器会返回同样的数据包以做应答

在 wireshark 上可以看到

1849 → 25565 [PSH, ACK] Seq=28 Ack=13939 Win=263424 Len=10
25565 → 1849 [PSH, ACK] Seq=13939 Ack=38 Win=42496 Len=10

数据 Data 均为 09010000000000002e5d

# 参考

MineCraft 协议 —— 握手篇(讲解 + 实现)

Protocol version numbers

Server List Ping

Status Response

Clientbound

What does the normal status ping sequence look like?

Protocol

VarInt and VarLong

varint 是啥你真的知道么?