排查 Windows 动态端口与保留端口冲突
太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处

排查 Windows 动态端口与保留端口冲突

我启动 Cocos CLI 时遇到了这个错误:

listen EACCES: permission denied 0.0.0.0:9230

9230 没有被其他进程监听,问题是它落进了 Windows 的 TCP 排除范围。继续检查后,我又发现系统的 TCP 动态端口被旧配置改成了 1024-15000

先看端口有没有被占用

Get-NetTCPConnection -LocalPort 9230 -ErrorAction SilentlyContinue |
  Format-Table LocalAddress,LocalPort,State,OwningProcess

有结果时,根据 PID 查进程:

Get-Process -Id <PID>

端口占用通常对应 EADDRINUSE。我这次没有监听进程,Node.js 返回的是 EACCES,所以继续检查系统保留范围。

查看动态端口和排除端口

netsh interface ipv4 show dynamicportrange protocol=tcp
netsh interface ipv6 show dynamicportrange protocol=tcp

netsh interface ipv4 show excludedportrange protocol=tcp
netsh interface ipv6 show excludedportrange protocol=tcp

现代 Windows 默认的 TCP 动态端口范围是:

Start Port      : 49152
Number of Ports : 16384

我的机器显示为:

Start Port      : 1024
Number of Ports : 13977

也就是 1024-15000。排除列表中还有一个覆盖 9230 的区间,因此这个端口没有进程占用,应用仍然无法监听。

检查 MaxUserPort

Get-ItemProperty `
  -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' `
  -Name MaxUserPort `
  -ErrorAction SilentlyContinue

我这里的值正好是 15000,与异常动态范围的上界一致。这个旧参数把 TCP 动态端口压到了开发服务常用的低位区域。

不能仅凭这一项判断是谁写入了配置。我检查时机器上同时有 Hyper-V、HNS、WinNAT 和 WSL,它们会使用排除端口,但没有证据说明其中某一个修改了 MaxUserPort。这也不是中病毒的证据。

删除旧配置并重启

先记下原值,然后在管理员 PowerShell 中执行:

Remove-ItemProperty `
  -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' `
  -Name MaxUserPort

Restart-Computer

我没有逐段删除排除端口。那些范围可能由 HNS、WSL 或 Hyper-V 重新创建,强行删除还可能影响它们的网络。

重启后再次检查:

netsh interface ipv4 show dynamicportrange protocol=tcp
netsh interface ipv6 show dynamicportrange protocol=tcp

IPv4 和 IPv6 TCP 应当恢复为 49152 起始、16384 个端口。最后直接测试 9230 能否监听:

node -e "const s=require('net').createServer();s.listen(9230,'0.0.0.0',()=>{console.log('bind OK');s.close()})"

看到 bind OK 后,再启动原来的开发服务。

如果需要恢复修改前的值,可以写回并重启:

New-ItemProperty `
  -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' `
  -Name MaxUserPort `
  -PropertyType DWord `
  -Value 15000 `
  -Force

Restart-Computer

这里的 15000 是我机器上的旧值,不是推荐值。其他机器应该使用自己事先记录的值。