
WSL下使用Next.js时遇到端口被WinNAT保留导致不能正常运行的问题,可通过重启WinNAT服务或保护端口解决。
我不知道算不算一个错误或者Bug,可能对WSL保留这些端口的初衷,没有查到,但是解决这个问题,让人感到莫名其妙。
我使用pm2管理nextjs和fastapi,感觉这两个服务处理起来轻轻松松,但是刚迁移到wsl,验证服务却显示504的错误。给我第一个的感觉,就是:
防火墙确认关闭了,pm2服务也是online(这online有些误导),但是查看端口3000,却没有踪迹,找不到进程。这是什么原因呢?
还是勤快些,一个服务一个服务的状态排查。
我使用ss和lsof等命令在wsl中查找,使用tcpview在windows11中查找,就是没有发现这个3000端口占用的进程,奇了怪了。
切换3001验证下:
问题依旧,这是怎么回事,难道wsl这么不稳定吗?
WSL2 底层依赖于 Windows 的 Hyper-V 虚拟机技术。为了让虚拟机、Docker 容器以及 Windows 宿主机之间能够顺畅地进行网络通信,Windows 运行着一个名为 WinNAT (Windows NAT) 的核心网络服务。
导致这个问题的罪魁祸首在于 WinNAT 的“动态端口保留”机制: 在系统启动、网络环境变化或休眠唤醒时,Hyper-V 会向宿主机(Windows)一口气申请并强行锁死一长串的随机端口段(例如 3000~3050,或者8000~8080),作为内部虚拟化服务的保留端口。我使用的FastAPI端口8000也是幸运,如果也出现already in use,我估计更让人怀疑切换到wsl是否正确。
这就导致了一个死局:
不要凭感觉,我们可以用一行命令让 Windows 交代它的“底细”。 打开 Windows 的 CMD 或 PowerShell(无需管理员权限),输入以下命令查看系统当前强行保留的端口段:
在输出的列表中,仔细寻找。如果你发现类似下面这样的信息:
协议 tcp 端口排除范围 开始端口 结束端口 -------------------------------- 2854 2953 2954 3053 * <-- 你的 3000 端口不幸落入了这个被锁死的区间! 5357 5357 *
只要你想要用的端口(如 3000)落在了上述显示的任何一个区间内,你就算重启一百次 Next.js 也是跑不起来的。
既然找到了元凶,解决起来就非常简单粗暴了。我们需要重启 WinNAT 服务,强制 Windows 释放当前胡乱占据的端口段,并重新分配。
切回你的 Windows 系统,按 Win 键搜索 “PowerShell”,务必右键点击并选择“以管理员身份运行”。
在蓝色的管理员 PowerShell 窗口中,依次执行以下两行命令:
你会看到提示“Windows NAT Driver 服务已成功停止”和“已成功启动”。
现在,那些被系统霸占的端口已经被全数吐出来了。切回你的 WSL 终端,再次运行你的启动命令:
你会发现,熟悉的 ready - started server on 0.0.0.0:3000, url: http://localhost:3000 终于出现了!
重启 WinNAT 虽然立竿见影,但这治标不治本。哪天你重启电脑,Windows 可能又会把 3000 端口给抢走。
如果你希望永久禁止 Windows 动你的 3000 端口,可以利用管理员权限主动把你需要的开发端口“保护”起来。
依然在管理员 PowerShell 中执行以下完整流程:
(注:numberofports=1 表示只保护 3000 这一个端口。如果你想保护 3000~3005,就改成 6。)
设置成功后,未来 Hyper-V 无论怎么动态抢占端口,都会乖乖绕开你指定的开发端口,你的 Next.js 项目在 WSL 里再也不会遭遇这种“幽灵端口占用”的灵异事件了!
总结: WSL 是个好东西,但虚拟化网络层的坑确实不少。掌握 netsh 排查和 winnat 重启大法,能帮你省下大把排错的时间。希望这篇文章能帮你脱离苦海,安心写 Bug写代码!