查端口占用必须用owningprocess或-l ni参数,90%的“僵尸”实为未退出干净的node.exe、code helper或npm子进程;正确做法是windows用get-nettcpconnection直出pid,macos/linux用lsof -ni :端口号精准定位。

查端口占用必须用 OwningProcess 或 -ni 参数,别靠 guess
报 EADDRINUSE 时,90% 的“僵尸”其实是上一次调试或 Live Server 没退出干净的 node.exe、Code Helper 或 npm 子进程——它们没死透,套接字还挂着。别用 ps aux | grep node 或任务管理器里扫一堆 node.exe 名字,容易误杀其他项目。
正确做法是直出真实 PID:
- Windows PowerShell:
Get-NetTCPConnection -LocalPort 3000 | Select-Object OwningProcess—— 输出的OwningProcess就是 PID,不用转换、不卡 DNS - macOS/Linux:
lsof -ni :3000(-n跳过 DNS,-i只筛网络连接) - 如果
lsof报 command not found:macOS 运行xcode-select --install,Linux 运行brew install lsof或apt install lsof
kill -9 是最后手段,先发 SIGTERM 再清子进程树
直接 kill -9 或 taskkill /f /pid 容易让 npm/yarn/子线程继续占端口,甚至锁住 node_modules/.bin 导致后续 install 失败。
安全顺序:
- macOS/Linux:
kill 1234(发 SIGTERM),等 2 秒无响应再kill -9 1234 - Windows:
taskkill /f /t /pid 1234——/t是关键,它会递归干掉所有子进程 - 更彻底清理 VS Code 自家进程:
killall -r "Code Helper|Code|Electron"(macOS/Linux)或taskkill /f /t /im Code.exe && taskkill /f /t /im CodeHelper.exe(Windows)
杀完立刻验证:Get-NetTCPConnection -LocalPort 3000 或 lsof -i :3000 应该无输出。有输出说明还有漏网之鱼。
launch.json 改 port 不影响 Live Server,配置项各管各的
改了 launch.json 里的 port 还报错?因为你动的是 Debugger 端口(比如 9229),但真正占着 3000/5500 的是 Live Server 插件或项目脚手架本身。
对应改法:
- Live Server:VS Code 设置里搜
liveServer.settings.port,填个空闲值如5501 - Node.js 调试:
.vscode/launch.json中确认"port": "${env:PORT}",而非硬编码成9229 - Codex 或 Remote-SSH:这类扩展的端口(如
1455)无法通过配置改,必须杀进程 - React/Vite 脚手架:
PORT=3001 npm start,比改package.json快得多
代码里加 graceful shutdown,别等下次再撞上
光靠外部杀进程治标不治本。Node 服务启动时没注册信号处理器,Ctrl+C 或 VS Code 终止调试时,server.listen() 的套接字根本没被 close,就进了 TIME_WAIT 状态,看起来像“僵尸”。
最小优雅关闭模板:
const server = http.createServer(handler);
server.listen(3000, () => console.log('✅ running on :3000'));
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
function shutdown() {
console.log('⚠️ shutting down');
server.close(() => {
console.log('✅ server closed');
process.exit(0);
});
}
注意:只监听 SIGINT 不够,VS Code 调试器发的是 SIGTERM;server.close() 必须带回调,否则 process.exit() 会提前退出,端口仍被锁。











