服务器查询
你可以通过 UDP/IP 数据包向正在运行的游戏服务器查询信息。本文档描述了访问这些数据所需的数据包格式和协议。
数据类型
所有服务器查询都由五种基础数据类型组合成数据流。所有类型均为小端序,这也是大多数语言在 x86 CPU 上的默认字节序。Java 用户应使用 ByteBuffer 进行转换。
| 名称 | 描述 |
|---|---|
| byte | 8 位字符或无符号整数 |
| short | 16 位有符号整数 |
| long | 32 位有符号整数 |
| float | 32 位浮点数 |
| long long | 64 位无符号整数 |
| string | 可变长度字节字段,使用 UTF-8 编码,以 0x00 结尾 |
协议
Steam 使用的数据包大小最大为 1400 字节 + IP/UDP 头。如果请求或响应的数据需要更多数据包承载,那么这些数据包会使用新的 IP/UDP 头部。
2023-07-10:已观察到 Rust 打破了这一规则,会发送最大 IP 大小(64KB)的数据包。
简单响应格式
| 数据 | 类型 | 说明 |
|---|---|---|
| Header | long | 始终等于 -1 (0xFFFFFFFF)。表示该响应未被拆分。 |
| Payload | ||
多包响应格式
Goldsource 服务器
| 数据 | 类型 | 说明 |
|---|---|---|
| Header | long | 始终等于 -2 (0xFFFFFFFE)。表示该数据包是拆分包。 |
| ID | long | 服务器为每次响应分配的唯一编号。 |
| Packet number | byte | 高 4 位表示当前数据包编号(从 0 开始),低 4 位表示数据包总数(2 到 15)。 |
| Payload | ||
Source 服务器
| 数据 | 类型 | 说明 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | long | 始终等于 -2 (0xFFFFFFFE)。表示该数据包是拆分包。 | |||||||||
| ID | long | 含义与 Goldsource 服务器相同。但是,如果 | |||||||||
| Total | byte | 此响应的数据包总数。 | |||||||||
| Number | byte | 当前数据包编号,从 0 开始。 | |||||||||
| Size | short | (仅 Orange Box 引擎及以上版本。) 发生分包前的最大数据包大小。默认值为 1248 字节 (0x04E0),但服务器管理员可以降低该值。对于较旧的引擎版本,数据包的最大和最小大小不可更改。已知不包含该字段的 AppID:215、17550、17700,以及 protocol = 7 时的 240。 | |||||||||
仅在响应的第一个数据包中存在,并且仅当响应被压缩时存在。
要解压此响应:收集并组装所有收到的数据包 payload,然后将其传入类似 C 语言中的 BZ2_bzBuffToBuffDecompress() 、PHP 中的 bzdecompress()],或 Python 中的bz2.decompress() 等函数。压缩似乎已经在较新的 Source 游戏中逐渐淘汰,但仍存在于约 2006 年时期的引擎中。 | |||||||||||
| Payload | |||||||||||
由于 UDP 数据包可能乱序到达或延迟到达,因此每个数据包都应通过其 “ID” 和 “Number” 进行验证!无论请求或响应是否带有此头部,后续都会跟随一个整数 -1 (0xFFFFFFFF) 和用户数据。 因此,通过读取前 4 个字节,即可判断数据是拆分响应 (-2),还是单个数据包响应 (-1)。
注意:已观察到部分游戏在使用拆分包方式时,会错误地在拆分 payload 内嵌套单消息头部。
请求
服务器会响应 5 种查询:
A2S_INFO- 服务器的基本信息。
A2S_PLAYER- 服务器上每个玩家的详细信息。
A2S_RULES- 服务器正在使用的规则。
A2A_PING- Ping 服务器。 (已废弃)
A2S_SERVERQUERY_GETCHALLENGE- 返回用于 player 和 rules 查询的 challenge number。 (已废弃)
查询应通过 UDP 数据包发送到服务器的监听端口。
Challenge 机制
服务器可能会立即返回数据。然而,由于该响应比请求更大,这会使服务器容易受到反射放大攻击。 因此,服务器可能会使用 S2C_CHALLENGE ('A' or 0x41) 向客户端返回一个 challenge。在这种情况下,客户端应将 challenge number 附加到请求后重新发送。 这一变更于 2020 年 12 月引入,用于解决反射攻击漏洞,建议所有客户端都支持新协议。更多信息请参见 this post 。
本文档之前的结构曾让人误以为只有 A2S_INFO 消息会以这种方式返回 challenge;然而,如果 challenge 被省略、错误或已过期,任何查询都可能返回 `S2C_CHALLENGE` 响应。
A2S_INFO
获取服务器信息,包括但不限于:服务器名称、当前正在运行的地图,以及玩家数量。
l4d2 server 2.2.1.3 update:一些服务器会发送 challenge number,这会导致 hlsw 无法获取服务器信息;
请求格式
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'T' (0x54) |
| Payload | string | "Source Engine Query\0" |
| Challenge | long | 4 字节 challenge(有时存在,见下方说明) |
FF FF FF FF 54 53 6F 75 72 63 65 20 45 6E 67 69 ÿÿÿÿTSource Engi 6E 65 20 51 75 65 72 79 00 ne Query. |
challenge number 示例:
服务器响应:
|
FF FF FF FF 41 0A 08 5E EA |
你需要发送:
|
FF FF FF FF 54 53 6F 75 72 63 65 20 45 6E 67 69 6E 65 20 51 75 65 72 79 00 0A 08 5E EA |
注意:未来请求中可能会附加额外数据!不要假设带有额外数据的请求就是伪造数据包!
注意:2021-6-19 13:05:43 变更:当 l4d2 服务器从同一 IP 地址收到某些数据包时,可能会拒绝回复。
注意:已观察到部分较旧游戏会同时回复 GoldSource INFO 数据包和较新的 INFO 数据包。
响应格式
| 数据 | 类型 | 说明 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | byte | 始终等于 'I' (0x49) | ||||||||||||
| Protocol | byte | 服务器使用的协议版本。 | ||||||||||||
| Name | string | 服务器名称。 | ||||||||||||
| Map | string | 服务器当前加载的地图。 | ||||||||||||
| Folder | string | 包含游戏文件的文件夹名称。 | ||||||||||||
| Game | string | 游戏完整名称。 | ||||||||||||
| ID | short | 游戏的 Steam Application ID。 | ||||||||||||
| Players | byte | 服务器上的玩家数量。 | ||||||||||||
| Max. Players | byte | 服务器报告可容纳的最大玩家数。 | ||||||||||||
| Bots | byte | 服务器上的 bot 数量。 | ||||||||||||
| Server type | byte | 表示服务器类型:
| ||||||||||||
| Environment | byte | 表示服务器操作系统:
| ||||||||||||
| Visibility | byte | 表示服务器是否需要密码:
| ||||||||||||
| VAC | byte | 表示服务器是否使用 VAC:
| ||||||||||||
只有当服务器正在运行 The Ship 时,响应中才存在以下字段:
| ||||||||||||||
| Version | string | 服务器安装的游戏版本。 | ||||||||||||
| Extra Data Flag (EDF) | byte | 如果存在,该字段指定后续将包含哪些附加数据字段。 | ||||||||||||
仅当 if ( EDF & 0x80 ) 为真时存在:
| ||||||||||||||
仅当 if ( EDF & 0x10 ) 为真时存在:
| ||||||||||||||
仅当 if ( EDF & 0x40 ) 为真时存在:
| ||||||||||||||
仅当 if ( EDF & 0x20 ) 为真时存在:
| ||||||||||||||
仅当 if ( EDF & 0x01 ) 为真时存在:
| ||||||||||||||
Counter-Strike: Source 响应示例:
FF FF FF FF 49 02 67 61 6D 65 32 78 73 2E 63 6F ÿÿÿÿI.game2xs.co 6D 20 43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65 m Counter-Strike 20 53 6F 75 72 63 65 20 23 31 00 64 65 5F 64 75 Source #1.de_du 73 74 00 63 73 74 72 69 6B 65 00 43 6F 75 6E 74 st.cstrike.Count 65 72 2D 53 74 72 69 6B 65 3A 20 53 6F 75 72 63 er-Strike: Sourc 65 00 F0 00 05 10 04 64 6C 00 00 31 2E 30 2E 30 e......dl..1.0.0 2E 32 32 00 .22. |
The Ship 响应示例:
FF FF FF FF 49 07 53 68 69 70 20 53 65 72 76 65 ÿÿÿÿI.Ship Serve 72 00 62 61 74 61 76 69 65 72 00 73 68 69 70 00 r.batavier.ship. 54 68 65 20 53 68 69 70 00 60 09 01 05 00 6C 77 The Ship.`....lw 00 00 01 03 03 31 2E 30 2E 30 2E 34 00 .....1.0.0.4. |
SiN 1 Multiplayer 响应示例:
FF FF FF FF 49 2F 53 65 6E 73 65 6D 61 6E 6E 20 ÿÿÿÿI/Sensemann 53 69 4E 20 44 4D 00 70 61 72 61 64 6F 78 00 53 SiN DM.paradox.S 69 4E 20 31 00 53 69 4E 20 31 00 1D 05 00 10 00 iN 1.SiN 1...... 6C 77 00 00 31 2E 30 2E 30 2E 30 00 lw..1.0.0.0. |
Rag Doll Kung Fu 响应示例:
FF FF FF FF 49 FC 54 68 65 20 44 75 64 65 27 73 ÿÿÿÿIüThe Dude's 20 64 6F 6A 6F 00 53 6F 63 63 65 72 00 52 44 4B dojo.Soccer.RDK 46 53 6F 63 63 65 72 00 52 61 67 44 6F 6C 6C 4B FSoccer.RagDollK 75 6E 67 46 75 3A 20 53 6F 63 63 65 72 00 EA 03 ungFu: Soccer.ê. 01 04 00 00 77 00 00 32 2E 33 2E 30 2E 30 00 ....w..2.3.0.0. |
过时的 GoldSource 响应
GoldSource 服务器会使用与 Source 服务器相同的响应。更旧的服务器和 Steam 之前的服务器仍会使用下面的响应格式。可以通过 AppID 区分 GoldSource 与 Source。
| 数据 | 类型 | 说明 | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | byte | 始终等于 'm' (0x6D) | ||||||||||||||||||||||||
| Address | string | 服务器的 IP 地址和端口。 | ||||||||||||||||||||||||
| Name | string | 服务器名称。 | ||||||||||||||||||||||||
| Map | string | 服务器当前加载的地图。 | ||||||||||||||||||||||||
| Folder | string | 包含游戏文件的文件夹名称。 | ||||||||||||||||||||||||
| Game | string | 游戏完整名称。 | ||||||||||||||||||||||||
| Players | byte | 服务器上的玩家数量。 | ||||||||||||||||||||||||
| Max. Players | byte | 服务器报告可容纳的最大玩家数。 | ||||||||||||||||||||||||
| Protocol | byte | 服务器使用的协议版本。 | ||||||||||||||||||||||||
| Server type | byte | 表示服务器类型:
| ||||||||||||||||||||||||
| Environment | byte | 表示服务器操作系统:
| ||||||||||||||||||||||||
| Visibility | byte | 表示服务器是否需要密码:
| ||||||||||||||||||||||||
| Mod | byte | 表示该游戏是否为 mod:
| ||||||||||||||||||||||||
只有当 “Mod” 为 1 时,响应中才存在以下字段:
| ||||||||||||||||||||||||||
| VAC | byte | 表示服务器是否使用 VAC:
| ||||||||||||||||||||||||
| Bots | byte | 服务器上的 bot 数量。 | ||||||||||||||||||||||||
响应示例:
FF FF FF FF 6D 37 37 2E 31 31 31 2E 31 39 34 2E ÿÿÿÿm77.111.194. 31 31 30 3A 32 37 30 31 35 00 46 52 20 2D 20 56 110:27015.FR - V 65 72 79 47 61 6D 65 73 2E 6E 65 74 20 2D 20 44 eryGames.net - D 65 61 74 6D 61 74 63 68 20 2D 20 6F 6E 6C 79 20 eatmatch - only 73 75 72 66 5F 73 6B 69 20 2D 20 6E 67 52 00 73 surf_ski - ngR.s 75 72 66 5F 73 6B 69 00 63 73 74 72 69 6B 65 00 urf_ski.cstrike. 43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65 00 0C Counter-Strike.. 12 2F 64 6C 00 01 77 77 77 2E 63 6F 75 6E 74 65 ./dl..www.counte 72 2D 73 74 72 69 6B 65 2E 6E 65 74 00 00 00 01 r-strike.net.... 00 00 00 00 9E F7 0A 00 01 01 00 ....ž÷..... |
A2S_PLAYER
此查询用于获取当前服务器上的玩家信息。它需要先执行一步来获取 challenge number。
请求格式
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'U' (0x55) |
| Challenge | int | Challenge number,或 -1 (0xFFFFFFFF) 用于接收 challenge number。 |
Challenge 响应格式:
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'A' (0x41) |
| Challenge | int | Challenge number。 |
Challenge 请求示例:
FF FF FF FF 55 FF FF FF FF ÿÿÿÿUÿÿÿÿ" |
Challenge 响应示例:
FF FF FF FF 41 4B A1 D5 22 ÿÿÿÿAÿÿÿÿ" |
带收到的 challenge number 的 A2S_PLAYER 请求示例:
FF FF FF FF 55 4B A1 D5 22 ÿÿÿÿUÿÿÿÿ" |
响应格式
| 数据 | 类型 | 说明 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | byte | 始终等于 'D' (0x44) | |||||||||||||||
| Players | byte | 已收集信息的玩家数量。 | |||||||||||||||
“Players” 中每个玩家都会有以下数据块:
| |||||||||||||||||
The Ship 的额外玩家信息(位于数据包中的基础信息之后):
| 数据 | 类型 | 说明 |
|---|---|---|
| Deaths | long | 玩家死亡次数 |
| Money | long | 玩家金钱 |
响应示例:
FF FF FF FF 44 02 01 5B 44 5D 2D 2D 2D 2D 3E 54 ÿÿÿÿD..[D]---->T 2E 4E 2E 57 3C 2D 2D 2D 2D 00 0E 00 00 00 B4 97 .N.W<----.....´— 00 44 02 4B 69 6C 6C 65 72 20 21 21 21 00 05 00 .D.Killer !!!... 00 00 69 24 D9 43 ..i$ÙC |
The Ship 响应:
FF FF FF FF 44 13 00 53 68 69 70 6D 61 74 65 31 ▒▒▒▒D..Shipmate1 00 00 00 00 00 00 00 80 BF 01 53 68 69 70 6D 61 .......▒▒.Shipma 74 65 32 00 00 00 00 00 00 00 80 BF 02 53 68 69 te2.......▒▒.Shi 70 6D 61 74 65 33 00 00 00 00 00 00 00 80 BF 03 pmate3.......▒▒. 53 68 69 70 6D 61 74 65 34 00 00 00 00 00 00 00 Shipmate4....... 80 BF 04 53 68 69 70 6D 61 74 65 35 00 00 00 00 ▒▒.Shipmate5.... 00 00 00 80 BF 07 28 31 29 4C 61 6E 64 4C 75 62 ...▒▒.(1)LandLub 62 65 72 00 00 00 00 00 D3 8E 68 45 00 00 00 00 ber.....ӎhE.... C4 09 00 00 00 00 00 00 C4 09 00 00 00 00 00 00 ▒.......▒....... C4 09 00 00 00 00 00 00 C4 09 00 00 00 00 00 00 ▒.......▒....... C4 09 00 00 00 00 00 00 C4 09 00 00 ▒.......▒... |
A2S_RULES
返回服务器规则,或者以名称/值对形式返回配置变量。此查询需要先执行 challenge 步骤。
包含的 控制台变量 是带有 FCVAR_NOTIFY 标记的变量,以及服务器配置中列出的其他变量。
请求格式
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'V' (0x56) |
| Challenge | long | Challenge number,或 -1 (0xFFFFFFFF) 用于接收 challenge number。 |
Challenge 响应格式:
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'A' (0x41) |
| Challenge | long | Challenge number. |
Challenge 请求示例:
FF FF FF FF 56 FF FF FF FF ÿÿÿÿVÿÿÿÿ" |
Challenge 响应示例:
FF FF FF FF 41 4B A1 D5 22 ÿÿÿÿVK¡Õ" |
包含收到的 challenge number 的最终请求:
FF FF FF FF 56 4B A1 D5 22 ÿÿÿÿVK¡Õ" |
响应格式
| 数据 | 类型 | 说明 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Header | byte | 始终等于 'E' (0x45) | |||||||||
| Rules | short | 响应中的规则数量。 | |||||||||
“Rules” 中每条规则都会有以下数据块:
| |||||||||||
A2A_PING
Ping 服务器以确认其是否存在,也可用于计算到服务器的延迟。
请求格式
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'i' (0x69) |
FF FF FF FF 69 ÿÿÿÿi |
响应格式
Goldsource 服务器
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'j' (0x6A) |
| Payload | string | Null |
FF FF FF FF 6A 00 ÿÿÿÿj. |
Source 服务器
| 数据 | 类型 | 值 |
|---|---|---|
| Heading | byte | 'j' (0x6A) |
| Payload | string | '00000000000000.' |
FF FF FF FF 6A 30 30 30 30 30 30 30 30 30 30 30 ÿÿÿÿj00000000000 30 30 30 00 000. |
A2S_SERVERQUERY_GETCHALLENGE
A2S_PLAYER 和 A2S_RULES 查询都需要 challenge number。过去,可以通过 A2S_SERVERQUERY_GETCHALLENGE 请求获取这个数字。在较新的游戏中,该请求已不再有效。现在应改为发送初始 challenge 为 -1 (0xFFFFFFFF) 的 A2S_PLAYER 或 A2S_RULES 查询,以接收可用的 challenge number。
在某些引擎上仍可使用该请求 (已确认的 AppID:17510, 17530, 17740, 17550, 17700) 。格式如下:
请求格式
| 数据 | 类型 | 值 |
|---|---|---|
| Header | byte | 'W' (0x57) |
FF FF FF FF 57 ÿÿÿÿW |
响应格式
| 数据 | 类型 | 说明 |
|---|---|---|
| Header | byte | 应等于 'A' (0x41)。 |
| Challenge | long | 要使用的 challenge number。 |
响应示例:
FF FF FF FF 41 4B A1 D5 22 ÿÿÿÿAK¡Õ" |
协议实现库
- @fabricio-191/valve-server-query Node.js 的 Server、Master Server 和 RCON 协议实现
- Steam Server Query 实现 Master Server Query Protocol 和 Game Server Queries 的 JavaScript(Node.js)模块
- Asynchronous Game Query Library 基于 Netty 的 Java 8 异步游戏查询库
- Source Server Query Library C 语言库
- libssq C 语言库
- GameServer Java 库
- QueryEd Java 库
- Steam Condenser Java、PHP 和 Ruby 库
- GameDig JavaScript(Node.js)模块
- Net-SRCDS-Queries Perl 模块
- a2squery Python 库
- SRCDSpy Python 库
- SourceLib Python 库
- python-valve Python 库
- Left4Query Python 库
- valve-range-query 用于 IP 范围查询的 Python 库
- python-a2s Python 库 (支持 async)
- dayzquery 库的附加组件,用于解析 A2S_RULES 响应中的 DayZ 特定二进制数据
- SourceStats Source 服务器统计工具
- PHP Source Query PHP 库
- SteamQuery.NET .NET/C# 封装库 (基于 .NET 9 / .NET Standard 2.0,由 cemahseri 开发。支持 Source、GoldSource 和已废弃的 GoldSource 协议。)
- QueryMaster C# 库
- QueryMaster C# 库 (面向 .NET Standard 2.0 的重制版)
- Okolni-Source-Query C# 库 (.NET Standard 2.0,现已支持 l4d2 2213)
- A2S 使用 .NET 5 的 C# 库。 (当前支持 A2S_Info 和 A2S_Player)
- aQuery VB.NET 库
- go-a2s Golang 库
- a2s-rs Rust 库
- a2s-go - A2S server queries 的 Golang 实现。也可通过 steam-go SDK addon 使用
- Rcon and A2s WebApi中文的WebApi协议库,基于Spring boot,仅支持A2S_INFO、A2S_PLAYER,Now Support l4d2 2213
- SteamPS 使用 PowerShell 作为 SteamCMD 封装的模块。在命令 Get-SteamServerInfo 中使用 server queries
- Mirai Tencent QQ robot基于mriaiBot适配了a2s rcon的腾讯qq机器人,仅支持A2S_INFO、A2S_PLAYER,Rcon,Now Support l4d2 2213
- Rust-GameDig Rust 库,支持完整协议 (也包含已废弃的 GoldSrc 响应)
- SteamQueryProxy 使用纯 C 为 Linux 编写的极高性能协议代理 (可用于缓解 VSE DoS 攻击,支持 Source 和 GoldSource)
- Steam2Go Golang 库。包含对 A2S、RCON 和多种 WebAPI 服务的支持
- ObjC Source Query Source Engine Query Protocol 和 Valve Master Server queries 的 ObjC 实现