当我们审视 DNS 系统的原始设计目标时,答案似乎很明确:将域名解析为 IP 地址。然而,总有一些工程师会提出那个危险的问题:「DNS 还能做什么?」DOOM Over DNS 项目给出的回答堪称工程界的黑色幽默 —— 用 DNS 协议完整运行经典游戏 DOOM。这个开源项目不仅展示了 DNS TXT 记录的潜在带宽,更为分布式系统工程师提供了关于非传统数据传输载体设计的宝贵案例。
核心架构与数据分片策略
DOOM Over DNS 的核心思路是将完整的游戏数据拆解为大量 DNS TXT 记录,通过客户端的 DNS 查询逐一获取并在内存中重组。项目的技术选型体现了对现实约束的精准把握:共享版 DOOM 的 WAD 文件加上基于 .NET 的游戏引擎 DLL,总计需要约 1964 个 TXT 记录来存储。这一数字并非随意设定,而是基于 Cloudflare 免费版 DNS 服务的单域记录上限与实际数据压缩率的综合考量。
在数据分片层面,项目采用了分层策略。第一层是游戏引擎的 .NET 程序集,这些 DLL 文件在发布时被编译为框架依赖模式(framework-dependent),而非原生 AOT 编译,因为原生 AOT 编译产出的二进制无法通过 Assembly.Load() 动态加载。引擎部分使用了 managed-doom 这个 C# 实现的 DOOM 引擎,并针对 DNS 加载场景进行了改造:移除了对 GLFW 的依赖,改为使用 Silk.NET 进行窗口管理,同时用内置的 NullSound 和 NullMusic 存根替代音频输出,以最大化减少依赖项。第二层是游戏内容数据,即 DOOM1.WAD 文件,这个包含关卡、sprite、纹理的核心文件同样被拆散存储。
分片的具体实现通过自定义的 TXTRecords PowerShell 模块完成。该模块负责将二进制数据转换为 Base64 编码的文本片段,并为每个片段生成唯一的子域名。分片大小受到 DNS 协议本身和 Cloudflare 服务限制的双重约束:免费版单域只能承载 185 个数据块,而付费版(Pro/Business/Enterprise)则放宽至 3400 个。这意味着单一一份 DOOM WAD 文件(需要约 1199 个块)对于免费用户而言必须跨多个域名分发,项目通过 -Zones 参数支持多域名的自动分片负载均衡。
查询协议与元数据管理
单纯将数据切割并存储只是问题的前半部分,客户端如何知道去哪里找、找什么顺序、何时算完成,这些问题需要一个完整的元数据层来解决。DOOM Over DNS 设计了一套基于 stripe-meta 的元数据管理机制。
当客户端启动时,它首先向指定的 PrimaryZone 发送查询请求,获取 stripe-meta 记录。这个元数据记录包含了所有数据分片的索引信息、校验哈希值、以及重组所需的顺序标记。客户端解析元数据后,会按照既定顺序发起大量的 DNS 查询请求,每个请求对应一个特定的 TXT 记录。项目的命名约定清晰而直接:-WadPrefix 参数默认为 doom-wad,用于 WAD 数据分片;-LibsPrefix 参数默认为 doom-libs,用于 DLL 捆绑包分片。
查询行为本身完全依赖标准 DNS 协议。客户端使用 PowerShell 7 的 Resolve-DnsName cmdlet 发出查询请求,这意味着整个数据传输过程不需要任何特殊的服务器端组件 —— 只需要一个能够托管 TXT 记录的 DNS 服务商。项目推荐使用 Cloudflare,其全球边缘节点可以免费提供 DNS 解析服务,且默认开启了缓存机制,这对于这种静态数据分发场景尤为有利。如果本地 DNS 解析器的缓存导致数据尚未同步,可以使用 -DnsServer '1.1.1.1' 参数直接指向 Cloudflare 的公共解析器。
值得注意的设计细节是断点续传能力。在上传阶段,如果传输过程被中断,Publish-TXTStripe 命令支持 -Resume 参数。它会验证已上传数据的哈希值,找到最后一个成功写入的分片位置,然后从该位置继续剩余工作。这对于需要上传数千条 DNS 记录的场景来说是一个实用的工程考量。
运行时加载与内存执行
最令人印象深刻的工程决策或许在于数据完全绕过了磁盘。整个游戏的 WAD 文件和 .NET 程序集都是在内存中完成组装和加载的,没有任何数据落地到本地文件系统。这不仅是一个技术噱头,在某些受限的安全环境中也具有实际意义 —— 假设一个系统禁止下载文件但允许 DNS 查询,这种传输方式就成为了一种潜在的解决方案。
客户端在获取所有必要的 TXT 记录后,首先将 DLL 捆绑包加载到当前 AppDomain 中。managed-doom 引擎的改造版本使用流式加载机制读取 WAD 数据,即边下载边解析,而非等待全部数据到齐后再处理。这种设计显著降低了初始加载延迟,用户可以更快地看到游戏画面,尽管在网络条件不佳时可能会遇到贴图或 sprite 缺失的短暂闪烁。
游戏启动参数通过 -DoomArgs 参数传入,支持标准的 DOOM 命令行选项。例如 -warp 1 3 -skill 5 可以直接跳转到第三关并以最高难度开局。项目的设计哲学是将游戏控制权完全交给用户,DNS 传输层仅负责可靠地将数据送达。
工程参数与监控要点
对于希望在生产环境中复现或扩展这一架构的工程师,以下参数值得重点关注。查询超时默认使用系统 DNS 解析器的超时设置,在网络不稳定环境下建议显式指定 -DnsServer 参数指向延迟更低的公共解析器。Cloudflare 免费版的 185 记录上限是硬性约束,若需存储更大体积的游戏或其他应用数据,必须规划多域名分片策略。上传阶段需要 Cloudflare API Token,且该 Token 必须具备「Edit zone DNS」权限,这是安全设计上的一个关键考量。
监控层面应关注 DNS 查询的成功率和响应延迟。由于每个游戏帧可能触发多次数据读取,任何单次查询的失败都会造成可感知的卡顿。建议在本地部署 DNS 转发器并开启查询日志,以便追踪丢包或超时模式。另一个可优化的方向是在客户端实现 DNS 预取 —— 在渲染当前帧的同时后台发起对后续数据块的查询请求,从而隐藏网络延迟。
DOOM Over DNS 项目不仅仅是一个极客式的娱乐产物,它本质上是对 DNS 协议数据传输能力的一次深度探索,也是对「常规协议非常规用法」这一工程思维的生动诠释。对于关注协议边界、边缘计算或隐蔽通道技术的开发者而言,这个项目提供了丰富的设计参考。
资料来源:该项目源码托管于 GitHub(resumex/doom-over-dns),当前已获得 429 颗星标,采用 C# 与 PowerShell 实现。