:A2S/FiveM/Minecraft服务器查询协议实现原理)
WindowsGSM源码解析二A2S/FiveM/Minecraft服务器查询协议实现原理【免费下载链接】WindowsGSM A powerful tool to manage game servers. Equipped with a GUI for server admins to install, import, start, stop, restart, update, and automate multiple servers with a push of a button.项目地址: https://gitcode.com/gh_mirrors/wi/WindowsGSMWindowsGSM 是一款面向游戏服管理员的免费图形化工具支持一键安装、启动、停止、更新和管理多台游戏服务器。在前一篇文章介绍了它的整体架构之后本篇深入 WindowsGSM 源码中的查询模块Query 目录拆解它是如何免费地在本地读出服务器状态的A2S 服务器查询协议如何与 Source/GoldSource 引擎通信、FiveM 的 getinfo 查询报文为什么简单到只有一行字符串、以及 Minecraft 服务器状态查询协议Query Protocol里那次标志性的握手挑战。理解这三套协议你就掌握了几乎所有主流游戏服状态面板的底层原理。WindowsGSM 查询模块的整体设计WindowsGSM 把所有询问服务器现在什么状态的逻辑集中在WindowsGSM/GameServer/Query/目录下目前包含三个类类协议典型使用场景A2S.csA2SSource/GoldSource 引擎查询CS:GO、Rust、ARK、CS1.6 等 30 款 Source/GS 系游戏FIVEM.csFiveMgetinfo查询GTA5FiveM服务器UT3.csMinecraft Query 协议与 UT3 同源Minecraft Java 版、Minecraft PE三者共享一个公共底座WindowsGSM/Functions/UdpClientHandler.cs。这是一个对 .NETUdpClient的轻量封装做且只做一件事——发一个 UDP 包、收一个应答并分别控制发送/接收超时SendTimeout/ReceiveTimeout。 为什么用 UDP 而不是 TCP因为服务器状态查询是一问一答的轻量场景UDP 无需三次握手、无连接状态服务器宕机或封端口时也能通过超时快速感知失败。所有游戏服类位于WindowsGSM/GameServer/目录通过一个dynamic字段声明自己用哪套协议例如GTA5.csQueryMethod new Query.FIVEM()MC.csQueryMethod new Query.UT3()Engine/Source.csQueryMethod new Query.A2S()dynamic在这里是刻意为之不同协议的返回结构不同字典、元组等用动态类型可以省去一堆接口抽象是 WindowsGSM 源码中很典型的实用主义写法。A2S 协议两阶段请求与二进制响应解析A2SAddress to Server是 Steam 系引擎的官方状态查询协议也是本模块中最复杂的实现。核心逻辑都在 A2S.cs 中围绕三个方法展开GetInfo()服务器信息、GetPlayer()在线玩家列表、GetPlayersAndMaxPlayers()人数摘要。A2S_INFO 请求字节头的秘密以GetInfo()为例整个流程分两步第 1 步构造请求包。报文以 4 个0xFF字节开头这是所有引擎查询包的魔数紧接着是协议头字符串TSource Engine Query\0注意末尾的空字符0xFF 0xFF 0xFF 0xFF TSource Engine Query\0第 2 步处理 Steam3 握手。如果服务器返回的第一个字节是0x41说明它要求 Steam3 二次握手——WindowsGSM 会把第一次的回应原样拼到请求尾部再发一次拿到真正的数据。这段重试逻辑是很多第三方 A2S 客户端容易踩的坑源码用responseData[0] 0x41一行判断就覆盖了。双引擎响应Source 与 GoldSource 的分支解析收到响应后跳过前 4 个0xFF第一个字节就是引擎签名0x49Source 引擎依次读出 协议号 → 服务器名 → 地图 → 游戏目录 → GameID → 玩家数 → 最大人数 → 机器人数 → 服务器类型d专用 /l监听→ 操作系统w/l→ 是否 VAC → 版本号再读一个EDF 标志位字节按位判断后面还挂没挂游戏端口、SteamID、观战端口、关键词等可选字段0x6DGoldSource 引擎字段顺序略有不同协议号挪到了后面且当是否为模组标志为真时还要额外读取模组的下载链接、版本号和大小。值得细看的是EDF 按位解析这一小段0x80位代表后面有游戏端口、0x10位代表后面有 64 位 SteamID、0x40位代表有观战信息、0x20位代表有关键词。这正是 A2S 协议自描述设计的精髓——用一个字节告诉客户端剩余数据长什么样。还有一个针对特定游戏的补丁当 GameID 关键词匹配到 Mordhau 时源码会从 Keywords 字段中解析B:数字标签来修正真实在线人数——因为该游戏默认报回的人数不可靠。这类协议之外的现实是维护游戏服管理器最有趣的部分。玩家列表Challenge 挑战机制GetPlayer()实现了带挑战码的两段式请求先发送0xFF×4 协议头 0xFF×4从回应中取出服务器下发的 challenge 字节串拼到UA2S_PLAYER 指令后重新发送才换来真正的玩家数据。每个玩家条目按 索引 → 名字 → 分数int32→ 在线时长float 秒 的顺序解析最终以(名字, 分数, 在线时长)元组存入字典。字符串读取统一用私有方法ReadString(BinaryReader br)循环读字节直到遇到0x00终止符再转成 UTF-8——这就是为什么 A2S 协议中所有字符串都是null 结尾格式。FiveM 查询一行字符串搞定 getinfo相比之下FIVEM.cs 的实现短到令人惊讶。FiveM 服务器本质上跑着一个兼容 Source 查询报文的 HTTP 风格接口请求同样以 4 个0xFF开头但后跟的载荷是纯 ASCII 字符串getinfo windowsgsm第二个词是客户端标识可随意取。响应服务器返回形如\FF\FF\FF\FFinfoResponse\n\key1\value1\key2\value2...的报文。WindowsGSM 跳过前 18 字节的固定头部把剩余内容按\切分成数组然后两两配对塞进字典[\\key1, \\value1, \\key2, \\value2, ...] ↓ 两两配对 key1 → value1, key2 → value2最终字典里能拿到hostname、gamename、clients当前人数、sv_maxclients最大人数、map、gamebranch等字段。GetPlayersAndMaxPlayers()直接取clients / sv_maxclients拼出12/64这样的人头显示。对比总结A2S 是严格对齐的二进制协议需要用BinaryReader逐字节解析FiveM 是人话协议一个字符串切分就能搞定。这也是为什么 GTA5 服务器在 WindowsGSM 中的查询代码只有 A2S 的三分之一。Minecraft 查询协议源自 Unreal Tournament 3 的握手挑战Minecraft 的状态查询协议常被叫作 Minecraft Query Protocol但它其实是Unreal Tournament 3 协议的变体——这也是 WindowsGSM 把实现类命名为UT3.cs见 UT3.cs的原因。Minecraft PE 版同样复用这个类。整个流程是一次典型的挑战—应答握手握手请求发送魔数0xFE 0xFD 指令字节0x09Handshake 4 字节会话 ID固定取0x10 0x20 0x30 0x40即可随意取值为真服务器回应 challenge返回包跳过 5 字节头部后剩下的 4 个 ASCII 字符是一个十进制整数challenge 值WindowsGSM 的GetToken()方法把它转回 4 个字节大端序正式查询发送魔数 指令字节0x00Server Query 同样的会话 ID challenge 令牌解析应答跳过 5 字节头部用 null 结尾字符串依次读出 MOTD公告栏、游戏类型、地图名、当前人数、最大人数再读一个 int16 游戏端口和 IP 字符串。这个先问出 challenge 再带着 challenge 查询的设计和 A2S 的 challenge 机制如出一辙——都是服务器用一次性随机值防止伪造/重放查询。理解了其中一套另一套自然触类旁通。协议如何接入主界面QueryMethod 的调用链三个协议类对外暴露统一的契约GetInfo()返回Dictionarystring, stringGetPlayersAndMaxPlayers()返回人数/上限字符串失败时静默返回null而不抛异常。主界面 MainWindow.xaml.cs 在刷新服务器列表时通过游戏服对象的QueryMethod动态调用这些方法把结果填进 DataGrid 的在线人数列。几个源码层面的通用设计值得学习超时兜底所有 UDP 收发都走 UdpClientHandler.csSendTimeout与ReceiveTimeout分离设置默认各 5 秒服务器没响应时查询只会优雅失败绝不卡死 UI 线程配合Task.Run异步执行异常全捕获每个协议的GetInfo()都包着 try/catch任何解析错位比如服务器返回了非预期格式都归结为null保证列表刷新不会因为一台坏服务器而中断查询端口换算ServerConfig.cs 中用一行魔法公式由主端口推算查询端口查询端口 ServerPort - 游戏默认端口 游戏默认查询端口让管理员只填一个端口号即可。小结三套协议、一个模式协议请求特征挑战机制解析方式源码文件A2S0xFF×4 协议头Steam3 握手 challengeBinaryReader 逐字段二进制解析WindowsGSM/GameServer/Query/A2S.csFiveM0xFF×4getinfo xxx无ASCII 字符串按\切分配对WindowsGSM/GameServer/Query/FIVEM.csMinecraft Query0xFE 0xFD 0x09 会话 ID十进制 challenge 令牌null 结尾字符串 二进制混合解析WindowsGSM/GameServer/Query/UT3.cs下一篇将转向 WindowsGSM 的进程与日志管理ProcessManagement如何可靠地启动、内嵌控制台并优雅停止服务器进程。【免费下载链接】WindowsGSM A powerful tool to manage game servers. Equipped with a GUI for server admins to install, import, start, stop, restart, update, and automate multiple servers with a push of a button.项目地址: https://gitcode.com/gh_mirrors/wi/WindowsGSM创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考