quake.exe 如何获得其 TCP/IP 协议栈
摘要
本文详细介绍了 id Software 如何利用 Windows 95 DPMI 服务器和 TCP/IP 协议栈,使 Quake 的可执行文件能够在 DOS 和 Windows 95 上运行,从而实现更好的多人联网游戏体验。
暂无内容
查看缓存全文
缓存时间: 2026/05/16 03:34
# quake.exe 如何获得其 TCP/IP 堆栈
来源:https://fabiensanglard.net/quake_chunnel/index.html
2025年11月17日
quake.exe 如何获得其 TCP/IP 堆栈
---
1996年6月发布的《雷神之锤》,在其生命周期中需要应对三次技术浪潮。除了3D硬件加速卡的出现和互联网的兴起,操作系统的转型也让游戏开发者陷入了困境。
随着微软推动Windows 95和Windows NT,它正在取代其传统的PC操作系统MS-DOS。1996年至1997年间,DOS的市场份额下降了50%。一些开发者,如暴雪北方,选择冒险,编写了仅适用于Windows 95的游戏,例如《暗黑破坏神》。而id Software则尽力制作了一个单一的二进制文件 `quake.exe`,使其既能运行在DOS上,也能运行在Windows上。
更令人印象深刻的是,id 还设法让《雷神之锤》在可用 Windows 95 TCP/IP 堆栈的情况下表现得更好。以下是他们做到这一点的过程。
## quake.exe 101
---
`quake.exe` 是一个 DOS 可执行文件。id Software 在《毁灭战士》中使用了 Watcom 编译器,但他们转而使用了一个名为 `djgpp` 的 GCC 移植版[\[1\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_1),在 Alpha 服务器上交叉编译《雷神之锤》。
```
$ file quake.exe
quake.exe: MS-DOS executable, COFF for MS-DOS, DJGPP go32 DOS extender
```
与 Watcom 的 `DOS/4GW` 类似,`djgpp` 为开发者提供了一个扩展器,允许编写使用平坦 32 位寻址的程序,而无需使用 DOS 原本强制要求的可怕的 16 位近/远实模式。扩展器由一个客户端和一个服务器组成。对于《雷神之锤》来说,扩展器客户端内嵌在 `quake.exe` 中,而服务器则位于 `cwsdpmi.exe` 中。
从开发初期开始,id 就要求 `djgpp` 的工程师确保他们的 DPMI 客户端能够在 `djgpp` 的 DPMI 服务器以及 Windows 95 的 DPMI 服务器上运行。
可能不太明显的是,`djgpp` 要让其 DPMI 客户端与另一个 DPMI 服务器协同工作是多么困难。但只要稍微了解一下它的工作原理,我就被深深震撼了。当时微软的内核工程师 Raymond Chen 对此情况有最精彩的描述:
> 客户端应用程序编写时假设它使用的是应用程序自带的 MS-DOS 扩展器,但实际上它正在与 Windows 自带的 DPMI 主机通信。
> 尽管程序在陌生的扩展器下运行,却似乎大多能正常工作——这要么令人完全震惊,要么完全显而易见,取决于你的观点。令人震惊是因为,你将一个为一种环境编写的程序放到另一种环境中运行。完全显而易见则是因为它们使用了相同的 DPMI 接口,只要接口行为一致,程序自然能继续工作——这就是接口存在的意义!
> —— Raymond Chen[\[2\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_2)
能够在 Windows 95 DPMI 服务器上运行,正是 `quake.exe` 实现同时支持 DOS 和 Windows 95 的关键。
## DOS 下的 quake.exe
---
《毁灭战士》运行时只需要两个文件:`doom.exe` 和 `doom.wad`,但《雷神之锤》附带了许多文件。
```
$ find quake
./mgenvxd.vxd ./genvxd.dll ./qlaunch.exe ./id1/pak0.pak ./pdipx.com
./cwsdpmi.exe ./q95.bat ./id1/config.cfg ./quake.exe ./quakeudp.dll
```
初看起来有点乱,但要在 DOS 下运行《雷神之锤》只需要四个文件,即游戏引擎 `quake.exe`、配置文件 `config.cfg`、资源文件 `pak0.pak` 和 DOS 扩展器服务器 `cwsdpmi.exe`。
```
./mgenvxd.vxd ./genvxd.dll ./qlaunch.exe ./id1/pak0.pak ./pdipx.com
./cwsdpmi.exe ./q95.bat ./id1/config.cfg ./quake.exe ./quakeudp.dll
```
### DOS 下的 quake.exe:多人游戏
《雷神之锤》支持四种多人游戏协议。
两种模式允许玩家进入决斗(1v1)。这两种模式都需要一个设备插入电脑的 COM 端口。调制解调器可以拨打对方的电话号码(唉,费钱啊),而零调制解调器电缆(这里称为“直接连接”)则要求两台电脑相距几英尺。
IPX 和 TCP/IP 则允许更有趣的死亡竞赛,最多支持 16 名玩家。IPX 技术面向局域网,所有机器彼此相距不远,而 TCP/IP 则允许连接世界各地的任何人。
请注意,在 DOS 下,默认情况下 IPX 和 TCP 模式都是禁用的(灰色显示)。
### DOS 下的 quake.exe:灰色显示的多人模式
《雷神之锤》附带 **PDIPX.EXE**,它加载了一个 IPX DOS TSR。该 TSR 与一个数据包驱动程序通信,后者再与网卡交互。《雷神之锤》能够检测该 DOS TSR,一旦检测到,就允许玩家选择 IPX。
使用 TCP/IP 几乎不可能。DOS 没有自带 TCP/IP 堆栈,而且它是一个足够复杂的东西,以至于只有一家供应商为 DOS 提供了 TSR。
这个 TSR 名叫 BWNFS。由 Beame & Whiteside 制作,1996 年售价 395 美元(2025 年相当于 830 美元!)[\[3\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_3)。可以合理地说,很少有玩家曾在 DOS 上使用 TCP/IP 玩《雷神之锤》。
## Windows 95 下的 quake.exe
从 Windows 95 启动 `quake.exe` 非常顺利。可执行文件被加载到一个 Windows 95 的“DOS 框”[\[4\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_4)中,该 DOS 框虚拟化了内存、中断和信号[\[5\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_5)。游戏的运行方式与 DOS 下完全相同,多人游戏选项也一样。这很方便,因为用户无需加载任何鼠标驱动程序或设置 `BLASTER` 环境变量来使声卡工作。
然而,这种方式运行《雷神之锤》需要 16 MiB 内存。但《雷神之锤》只需要 8 MiB,Windows 95 带来了相当大的开销!这里使用的文件与从 DOS 运行时相同,除了 `cwsdpmi.exe`,因为 DJGPP 客户端会检测并使用 Windows 内置的 DPMI 服务器。
```
./mgenvxd.vxd ./genvxd.dll ./qlaunch.exe ./id1/pak0.pak ./pdipx.com
./cwsdpmi.exe ./q95.bat ./id1/config.cfg ./quake.exe ./quakeudp.dll
```
得知 Windows 95 在虚拟机中运行 DOS 可执行程序,而《雷神之锤》却能全速运行,这令人印象深刻。我猜测,在全屏模式下,对 VGA 的内存读写被直接授予硬件访问权限,以保证性能。
## 神奇的 q95.bat 脚本
从 DOS 或 Windows 启动 `quake.exe` 并不是运行《雷神之锤》仅有的两种方式。还有第三种方式:运行 `q95.bat`。
在这种情况下,Windows 95 桌面上会短暂弹出一个“启动 Quake”窗口。
文本给出了正在发生的事情的线索。Quake 加载时带有一个通往 Winsock(微软的 TCP/IP 堆栈)的隧道。还有进一步的提示说明*什么*在干这件事:“Powered by Mpath”。但没有更多内容来解释这一切*如何*工作。
## Mpath
Mpath Interactive 是一家专注于在线游戏的公司。他们提供订阅服务帮助玩家互相寻找,同时也作为 ISP 转售商运营。[\[6\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_6)。帮助游戏公司发布支持互联网游玩的游戏符合他们的利益,正如当时 Mpath 的员工 Larry Hastings 回忆的那样:
> 在 90 年代中期那个原始的网络混沌时期,在线多人游戏仍处于萌芽阶段。如果你想在互联网上玩多人游戏,要么需要有明确的主机和端口信息,要么需要使用在线多人游戏服务。而在 1995 年,只有两家这样的服务:我们和 Total Entertainment Network。你可能会认为游戏创造者会来找我们说“请把我的游戏放到你的服务上!”,但是……不!我们不仅有授权团队出去为我们的服务获取游戏授权合同,而且我们还得向厂商支付将其游戏授权给我们的权利,这通常是独家授权。所以,我们拿到了《雷神之锤》和《虚幻》;TEN 得到了《毁灭公爵3D》和《纳斯卡赛车》。
>
> Mplayer 的用户体验是这样的。首先,你运行一个叫做“Gizmo”的 Windows 程序,它充当一种游戏浏览器。它知道你安装了哪些兼容的游戏,并让你浏览每个游戏可用的多人游戏;我们对此使用的比喻是“房间”。Quake 是即开即玩的(drop-in),所以你只需找到正在进行的游戏并直接加入——这在当时不是很多游戏都有的功能。或者,你可以找到一个“房间”,有人提议很快开始一场游戏。你也可以创建自己的房间。你可以设置房间名称,Mplayer Gizmo 有一些针对每款游戏的 UI,让你设置游戏设置(地图、功能等)。房间支持文字和语音聊天,甚至还有一个共享的“白板”,一个简单的画图程序。一旦“房间”的“主人”“启动”了游戏,每个人的 Gizmo 都会自动为他们启动游戏,并且游戏会自动加入那个在线游戏并开始游玩。
>
> 为了让一款游戏能在 Mplayer 上运行,它必须与 Mplayer 软件栈集成。大部分集成工作是由 Mpath 的工程师完成的;我们会从游戏开发者那里拿到源代码,“移植工程师”会让它在 Mplayer 上运行。这通常包括修改客户端和服务器,使两者都能通过 Mplayer 的服务器进行通信。早期版本的《雷神之锤》只支持 DOS,并使用“通道”(Chunnel)来与 Windows 95 TCP/IP 堆栈通信。(回过头看,这使得“Chunnel”成为一种“thunk”,类似于微软的“Win32s”。)我认为交易是这样的:我们将 Chunnel 授权给 id,作为回报,我们让《雷神之锤》出现在 Mplayer 上。因此,DOS 版《雷神之锤》除了能够通过主机和端口连接到互联网上的开放游戏服务器之外,还支持通过 Chunnel 在 Mplayer 上运行。
> —— Larry Hastings(电子邮件交流)
Larry 好心分享了一些关于《雷神之锤》的轶事。
> 在我们拿到游戏的首个构建版本后不久的一个下午,我们和 id 团队通过互联网进行了一局死亡竞赛。我们在加州库比蒂诺,位于 Bandley Drive 的一栋楼里(现在是苹果员工的“健身中心”)。他们当然在德克萨斯州梅斯基特。没错,这是通过互联网进行的死亡竞赛——非常令人兴奋!
> 我唯一确定参与游戏的 id 员工是 Tim Willits。他完全碾压了我们,一方面是因为他更熟悉《雷神之锤》,但也因为他知道所有秘密的位置。有一次我发现他从一个秘密通道里出来,手里拿着火箭发射器。要么他没看见我,要么我很快就死了。
> —— Larry Hastings(电子邮件交流)
至于解释 Chunnel 的工作原理,我就无能为力了。
> 我没有参与 Chunnel 的工作。那主要是一个名叫 Henry 的英国人负责的,但我记不住他的姓氏了,已经三十年了。关于他我只记得他的长相,以及他开着一辆很酷的车——一辆白色的 Merkur XR4Ti。
> —— Larry Hastings(电子邮件交流)
## Ghidra
当其他一切手段都失败时,我们还有 Ghidra 和 doomworld 上令人惊叹的社区(感谢 xttl[\[7\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_7))。经过大量的反编译和讨论,结果发现之前忽略的所有文件都是 Mpath 的“Chunnel”的一部分。
```
./mgenvxd.vxd ./genvxd.dll ./qlaunch.exe ./id1/pak0.pak ./pdipx.com
./cwsdpmi.exe ./q95.bat ./id1/config.cfg ./quake.exe ./quakeudp.dll
```
`q95.bat` 只是一个小脚本,用于启动 Mpath 的主程序。`qlaunch.exe` 包含了所有的 MPlayer 函数。然而,这个可执行文件的作用是有限的。
它仅仅加载了 `quakeudp.dll`。尽管名字令人困惑,但这个 DLL 是 Quake Chunnel 的核心。它是通往微软 TCP/UDP/IP 堆栈(`wsock32.dll`)的桥梁。它还会使用 `-path` 参数启动 Quake,使其加载 BSD 网络套接字 API `sys/socket.h`。最后,它也加载虚拟设备驱动程序管理器 `genvxd.dll`。
这个虚拟设备是窍门所在,它允许在 Windows 95 DOS 框内运行的 DOS 可执行文件与 win32 通信。动态库 `genvxd.dll` 加载一个名为 `GENVXD.VXD` 的虚拟设备驱动程序[\[8\]](https://fabiensanglard.net/quake_chunnel/index.html#footnote_8),该驱动程序安装自身以响应中断 `0x48`。
拼图的最后一块在 Quake 方面。BSD `sys/socket.h` 的实现是 `mpplc.c`,这是 Mpath 提供的代码。它负责编组每个 BSD 套接字函数调用,然后利用 DPMI 客户端触发一个软件中断,该中断在 win32 领域被接收。数据沿着我们之前描述的管道向上传递,直到被 `genvxd.dll` 解组并路由到 `wsock32.dll`。请注意 `mplib.c` 中编组函数与 `genvxd.dll` 中解组函数之间的对称性。
看起来 John Cash 参与了 Mpath 相关内容的编译。我们可以在 `mgenvxd.vxd` 的符号中找到他的名字。
```
F:\cashcode\GENVXD\bin\Mgenvxd.pdb
```
`mgenvxd.vxd`、`genvxd.dll`、`qlaunch.exe` 和 `quakeudp.dll` 的源代码从未发布过。这是 Mpath 专有的、已获专利的技术。很可能 id 只获得了发布其客户端部分的许可。
据我所知,这就是《雷神之锤》能够通过 IP 发送 TCP 和 UDP 数据包的方式。当 id 停止发布 DOS 可执行文件后(最后一个版本是 `vquake.exe`),这个复杂的结构就过时了。1996年12月之后,`winquake.exe`、`glquake.exe` 以及所有 QuakeWorld 二进制文件都是仅限 win32 的,可以直接访问 `wsock32.dll`。
## 参考文献
---
[1] 为什么 id 为《雷神之锤》选择了 DJGPP? (https://groups.google.com/g/comp.os.msdos.djgpp/c/VD-mIK56h74/m/P5zE_uabjnEJ)
[2] 让 MS-DOS 游戏在 Windows 95 上运行 (https://devblogs.microsoft.com/oldnewthing/20160328-00/?p=93204)
[3] Mac 和 PC TCP/IP FAQ (https://www.atmos.albany.edu/ftp/gopher-data/faq/Mac_PC_TCP-IP_FAQ?utm_source=chatgpt.com#:~:text=BWNFS%20%20%20%20%20%20%20%20%20%20%20Beame%20%26%20Whiteside%20%20%20%20%20%20*%24395)
[4] Microsoft Windows Chicago Reviewer's Guide, 第69页 (https://fabiensanglard.net/quake_chunnel/win95_reviewer_guide.pdf)
[5] 编写 Windows VxD 和设备驱动程序 (https://www.amazon.com/Writing-Windows-VxDs-Device-Drivers/dp/0879304383)
[6] MPlayer.com (https://en.wikipedia.org/wiki/MPlayer.com)
[7] 《雷神之锤》如何在 Windows 95 上运行? (https://www.doomworld.com/forum/topic/153992-how-did-quake-work-on-windows-95/)
[8] 虚拟设备驱动程序——什么是 VxD? (https://fabiensanglard.net/quake_chunnel/win95_reviewer_guide.pdf)
---
相似文章
WinQuake 存在的原因及其工作原理
深入探讨创建 WinQuake(Quake 的 Windows 原生版本)的历史原因,以及它如何在 Windows 95 和 NT 上实现接近 DOS 版本的性能。
打造一台1997年雷神之锤PC:雷神之锤基准测试
针对各种1990年代CPU和配置下雷神之锤性能的详细技术分析,比较英特尔、Cyrix、AMD芯片以及DOS和Windows 95下的内存类型。
像1997年那样编译Quake!
一份详细的指南,介绍如何重现使用Windows NT 4和Visual C++ 6等老式工具编译Quake的win32二进制文件的过程(就像1997年所做的那样)。
打造一台1997年的Quake PC!
作者记录了打造一台1997-1998年古董PC的过程,用于运行所有版本的Quake,涵盖了硬件选择,如Pentium MMX、3dfx Voodoo2和Socket 7主板。
打造一台1997年的Quake PC:对GLquake进行基准测试
一位开发者讲述了他组装一台配备3dfx Voodoo显卡的复古PC,并对GLQuake进行基准测试的过程,同时讨论了硬件的小问题和性能印象。