一个奇怪的问题
在用 Windows 的时候遇到一个特别烦躁的 bug,有些需要绑定端口的程序老是启动不了,比如 Clash,Wezterm,查询程序的启动日志,发现是无法绑定端口,程序有时会提示是端口已经被使用,所以考虑去手动把占用端口的程序关掉,但是使用 netstat -ano|findstr "<port>"
的命令去查询是哪个进程在占用,却怎么也找不到。因为 Windows 确实用的时间不多,问题出现也很随机,所以有段时间我的解决方案都是简单地换端口多试几次(有时换地端口也有问题,要多试几次)。但最近用 Wezterm 和 WSL 的次数有所增加,这个问题就越发地烦人了,基本每次启动都要试很多次,所以决定好好查查这个问题到底是怎么回事。
问题原因
造成这个 bug 有三个方面:
第一,Windows 中会为一些专用的需要端口的程序设置一个专门的可分配端口的范围,叫做动态端口范围,这个范围内的端口号可以被预先申请保留,保留之后相当于被占用,别的程序无法使用。这个范围默认值是 49152-65535,这个范围实际上是没有在 IANA 注册的端口号,平时基本不会用到,可以使用命令 netsh int ipv4 show dynamicport tcp
查询当前的范围。
第二,使用 Hyper-V 的 WSL 就是这么个程序,Hyper-V 启动之后就会在动态端口范围内随机的预先保留一些端口段,以供后续 WSL 使用。
第三,由于某些 Windows 自身的 bug,可能是 Windows Update 的问题,导致这个动态范围的值被错误的修改,从 49152-65535 修改旧 Windows 默认的开始端口加新的端口范围,成了 1024-15001 这个比较常用的范围,这样很多程序在申请端口时会刚好撞上已经被 Hyper-V 预先保留的端口范围,导致失败,并且无法通过 netstat -ano
查找到占用的程序,因为端口只是被预留了而没有真正的在使用中。
解决办法
知道原因之后,修复的方法也很简单,只需要将范围重新设置为默认值,或者个人不常用的范围就好。
首先,使用 netsh int ipv4 show dynamicport tcp
确认下当前的范围是不是错的:
PS C:\Users\max> netsh int ipv4 show dynamicport tcp
协议 tcp 动态端口范围
---------------------------------
启动端口 : 1024
端口数 : 13977
通过 netsh int ipv4 show excludedportrange protocol=tcp
命令可以查询当前已被保留的端口范围,可以确认自己要使用的端口是不是已经预先被保留了。
PS C:\Users\max> netsh int ipv4 show excludedportrange protocol=tcp
协议 tcp 端口排除范围
开始端口 结束端口
---------- --------
1026 1125
1126 1225
1226 1325
3262 3361
3462 3561
3662 3761
5357 5357
5628 5727
5728 5827
5828 5927
5928 6027
6228 6327
6328 6427
6449 6548
6549 6648
7018 7117
7118 7217
50000 50059 *
* - 管理的端口排除。
通过下面的命令重新设置 TCP 动态端口范围
netsh int ipv4 set dynamic tcp start=49152 num=16383
netsh int ipv6 set dynamic tcp start=49152 num=16383
重新设置之后重启 winnat 服务的办法来使得新的范围生效
net stop winnat
net start winnat
或者也可以直接重启电脑。