服务器列表 Ping
服务器列表 Ping(Server List Ping,SLP)是 Minecraft 服务器提供的一个接口,可以通过常规端口查询 MOTD、玩家数量、最大玩家数和服务器版本。SLP 是协议的一部分,与查询(Query)不同,该接口始终启用。官方客户端使用此接口显示多人游戏服务器列表,因此得名。SLP 在 1.7 版本中进行了不向后兼容的更改,但当前服务器仍然同时支持新协议和旧协议。
当前版本(1.7+)
使用常规客户端-服务器协议。关于通用数据包格式,请参见 协议 页面。
握手(Handshake)
首先,客户端发送一个 Handshake 数据包,状态设置为 1。
| 数据包 ID | 字段名 | 字段类型 | 说明 |
|---|---|---|---|
| 0x00 | Protocol Version | VarInt | 参见协议版本号。客户端计划用于连接服务器的版本(对于 ping 不重要)。如果客户端正在通过 ping 来确定使用的版本,按惯例应设为 -1。 |
| Server Address | String | 主机名或 IP,例如 localhost 或 127.0.0.1 |
|
| Server Port | Unsigned Short | 默认端口为 25565 | |
| Next state | VarInt | 应设为 1(表示状态) |
请求(Request)
客户端接着发送一个 Request 数据包。此数据包无字段。
| 数据包 ID | 字段名 | 字段类型 | 说明 |
|---|---|---|---|
| 0x00 | 无字段 |
响应(Response)
服务器应发送一个 Response 数据包。注意:官方服务器会等待接收接下来的 Ping 数据包最多 30 秒,超时后再发送 Response。
| 数据包 ID | 字段名 | 字段类型 | 说明 |
|---|---|---|---|
| 0x00 | JSON Response | String | 见下文 |
JSON Response 字段是一个 JSON 对象,格式如下:
{
"version": {
"name": "1.8.7",
"protocol": 47
},
"players": {
"max": 100,
"online": 5,
"sample": [
{
"name": "thinkofdeath",
"id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
}
]
},
"description": {
"text": "Hello world"
},
"favicon": "data:image/png;base64,<data>"
}
description字段是一个聊天组件对象。官方服务器无法提供实际的聊天组件数据,而是在对象的 text 中嵌入基于符号(§)的代码。favicon字段为可选。sample字段必须设置,但可以为空。favicon应为 PNG 图片,经过 Base64 编码,并加上data:image/png;base64,前缀。
收到 Response 数据包后,客户端可以发送下一个数据包以计算延迟,或者如果只关心上述信息,可以在此处断开连接。
如果客户端未收到格式正确的响应,则会尝试传统 ping。
Ping
如果继续流程,客户端将发送一个 Ping 数据包,包含一些不重要的有效载荷。
| 数据包 ID | 字段名 | 字段类型 | 说明 |
|---|---|---|---|
| 0x01 | Payload | Long | 可以是任意数字。官方客户端使用系统相关的时间值(毫秒)。 |
Pong
服务器将响应 Pong 数据包,然后关闭连接。
| 数据包 ID | 字段名 | 字段类型 | 说明 |
|---|---|---|---|
| 0x01 | Payload | Long | 应与客户端发送的相同 |
示例
C#、Java、Python、Python3、PHP 的示例实现可参考原始文档(原文提供了链接)。
1.6 版本
1.6 版本使用与 Netty 重写前兼容的协议。现代服务器通过起始字节 fe(而非通常的 00)识别此协议。
客户端到服务器
客户端在标准端口上发起 TCP 连接,发送以下十六进制数据(不进行身份验证和登录):
FE— 服务器列表 ping 的数据包标识符01— 服务器列表 ping 的有效载荷(始终为 1)FA— 插件消息的数据包标识符00 0B— 后续字符串长度(字符数),以 short 表示(始终为 11)00 4D 00 43 00 7C 00 50 00 69 00 6E 00 67 00 48 00 6F 00 73 00 74— 字符串MC|PingHost的 UTF-16BE 编码XX XX— 剩余数据长度,以 short 表示。计算公式为7 + len(hostname),其中len(hostname)是 UTF-16BE 编码主机名的字节数XX— 协议版本,例如 1.6.4 为4a(即 74)XX XX— 后续字符串长度(字符数),以 short 表示...— 客户端连接的主机名,UTF-16BE 编码XX XX XX XX— 客户端连接的端口,以 int 表示
所有数据类型均为大端序。
示例数据包转储:
0000000: fe01 fa00 0b00 4d00 4300 7c00 5000 6900 ......M.C.|.P.i.
0000010: 6e00 6700 4800 6f00 7300 7400 1949 0009 n.g.H.o.s.t..I..
0000020: 006c 006f 0063 0061 006c 0068 006f 0073 .l.o.c.a.l.h.o.s
0000030: 0074 0000 63dd .t..c.
服务器到客户端
服务器响应一个 0xFF 踢出数据包。数据包以单字节标识符 ff 开头,后跟一个两字节大端 short,表示后续字符串的字符长度。实际上可以忽略长度,因为服务器在发送响应后会关闭连接。
前 3 个字节之后,数据包是一个 UTF-16BE 字符串。它以两个字符开头:§1,后跟一个空字符。在传输中看起来像 00 a7 00 31 00 00。
剩余部分由空字符(即 00 00)分隔的字段:
- 协议版本(例如 74)
- Minecraft 服务器版本(例如 1.8.7)
- MOTD(例如 A Minecraft Server)
- 当前玩家数
- 最大玩家数
整个数据包类似:
<---> first character
0000000: ff00 2300 a700 3100 0000 3400 3700 0000 ....§.1...4.7...
0000010: 3100 2e00 3400 2e00 3200 0000 4100 2000 1...4...2...A. .
0000020: 4d00 6900 6e00 6300 7200 6100 6600 7400 M.i.n.e.c.r.a.f.
0000030: 2000 5300 6500 7200 7600 6500 7200 0000 .S.e.r.v.e.r...
0000040: 3000 0000 3200 30 0...2.0
注意:当在 1.7.x 及以上版本的服务器上使用此协议时,响应中的协议版本(第一个字段)始终为 127(并非真实协议号),因此旧客户端会认为此服务器不兼容。
示例
Ruby、PHP 的示例实现可参考原始文档。
1.4 至 1.5
在 Minecraft 1.6 之前,客户端到服务器的操作更简单,仅发送 FE 01,不带后续数据。
示例
PHP、Java、C# 的示例实现可参考原始文档。
Beta 1.8 至 1.3
在 Minecraft 1.4 之前,客户端仅发送 FE。
此外,服务器响应仅包含由 § 分隔的 3 个字段:
- MOTD(例如 A Minecraft Server)
- 当前玩家数
- 最大玩家数
整个数据包类似:
<---> first character
0000000: ff00 1700 4100 2000 4d00 6900 6e00 6500 ....A. .M.i.n.e.
0000010: 6300 7200 6100 6600 7400 2000 5300 6500 c.r.a.f.t. .S.e.
0000020: 7200 7600 6500 7200 a700 3000 a700 3100 r.v.e.r.§.0.§.1.
0000030: 30 0
评论 (0)
暂无评论,快来发表第一条评论吧!
发表评论
请先登录后再发表评论
去登录