我的语音合成服务连续几天启动失败。日志里只有一句 bind: EACCES,但端口明明没人用。这个坑我查了很久,最后发现是 Windows 的一个隐藏机制:保留端口段。
现象
服务是一个本地 TTS 服务器,要绑 8721 端口。每次启动都失败,报 EACCES(权限被拒绝)。
奇怪的是:
netstat显示 8721 没有任何进程占用- 换到
9221立刻就能绑上 - 用管理员权限运行也一样失败
根因
执行 netsh interface ipv4 show excludedportrange protocol=tcp,你会看到一堆「排除端口段」:
8617 - 8716
8717 - 8816
8817 - 8916
9015 - 91148721 正好落在 8717–8816 这个区间里。
这些端口被 Windows 系统服务(通常是 Hyper-V / WSL / Docker 相关的端口预留机制)保留了。普通进程去绑,系统直接返回 EACCES,而不是 EADDRINUSE。所以你看端口是「空」的,却绑不上。
实测验证:
| 端口 | 结果 |
|---|---|
| 8721 / 8722 / 8723 | EACCES |
| 8716 / 8816 / 8817 / 9114 | EACCES |
| 9221 / 9222 / 7321 / 18721 | ✅ OK |
连锁反应
更糟的是,我配了一个「看门狗」计划任务,每 60 秒检查一次 8721 端口通不通,不通就拉起服务。
于是形成了一个死循环:
- 检查 8721 → 不通
- 启动 TTS 服务 → 加载 3.5 GB 模型 → 占 4.1–4.5 GB 内存
- GPU 预热约 40 秒
- 绑端口 → 失败 → 进程退出
- 回到第 1 步
日志里一天出现了 244 次失败启动。内存被反复吃掉又释放,机器卡得不行。
修复
把配置里的端口从 8721 改成 9221,三处:
endpointspeech.localProcess.endpoint- 看门狗脚本的检查端口
改完,服务常驻,看门狗不再空转,卡顿和黑框一起消失。
教训
EACCES不等于「权限不够」。 在 Windows 上,它也可能是「端口被系统保留」。- 遇到端口绑定失败,先查保留段:
netsh interface ipv4 show excludedportrange protocol=tcp。 - 看门狗要有「启动失败次数上限」。 否则一次配置错误就能变成 244 次无效启动。
- 换端口时,把所有相关配置一起换。 漏一个就还是连不上。
这个坑不深,但很隐蔽。写出来,希望对同样用 Windows 跑本地服务的人有用。