Back

修复错误:'listen EADDRINUSE: address already in use' 在 Node.js 中

修复错误:'listen EADDRINUSE: address already in use' 在 Node.js 中

令人头疼的 EADDRINUSE 错误总是在你最意想不到的时候出现。当你准备启动 Node.js 服务器时,却没有看到应用程序运行,而是收到:Error: listen EADDRINUSE: address already in use :::3000。这个 Node.js EADDRINUSE 错误意味着另一个进程已经在使用你试图绑定的端口。以下是如何快速识别和修复这个 Node.js 端口冲突的方法。

关键要点

  • EADDRINUSE 错误发生在端口已被另一个进程占用时
  • 在 macOS/Linux 上使用 lsof,在 Windows 上使用 netstat 来识别阻塞进程
  • 使用 PID 终止进程或实现优雅关闭处理程序
  • 通过动态端口选择和正确的信号处理来预防未来的冲突

什么导致了 EADDRINUSE 错误?

listen EADDRINUSE 错误发生在你的 Node.js 应用程序试图绑定到一个已被占用的端口时。常见的罪魁祸首包括:

  • 服务器的前一个实例仍在运行
  • 另一个应用程序使用相同的端口
  • 崩溃的 Node.js 应用程序留下的僵尸进程
  • 热重载工具(如 nodemon)未能清理
  • IDE 集成终端保持进程处于活动状态

如何查找哪个进程正在使用端口

在你能够释放被占用的端口之前,需要识别是什么在使用它。不同操作系统的命令有所不同。

macOS 和 Linux

使用 lsof 查找进程:

lsof -i :3000

这会返回包括 PID(进程 ID)在内的进程详细信息:

COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
node     12345   you   11u  IPv6 0xac745b2749fd2be3      0t0  TCP *:3000 (LISTEN)

Windows

使用 netstat 识别进程:

netstat -ano | findstr :3000

输出在最后一列显示 PID:

TCP    0.0.0.0:3000    0.0.0.0:0    LISTENING    12345

如何终止进程并释放端口

一旦获得了 PID,就可以终止进程以释放端口。

macOS 和 Linux 解决方案

快速单行命令:

lsof -ti:3000 | xargs kill -9

手动方式:

kill -15 12345  # 优雅关闭(推荐)
kill -9 12345   # 如果 -15 不起作用则强制终止

使用 fuser 的替代方法:

sudo fuser -k 3000/tcp

Windows 解决方案

命令提示符:

taskkill /PID 12345 /F

PowerShell:

Stop-Process -Id 12345 -Force

图形界面方法: 打开任务管理器(Ctrl+Shift+Esc),找到 Node.js 进程并结束它。

预防未来的端口冲突

实现优雅关闭

向你的 Node.js 服务器添加适当的清理处理程序以防止端口错误:

const server = app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

// 处理终止信号
process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Server closed gracefully');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  server.close(() => {
    console.log('Server closed gracefully');
    process.exit(0);
  });
});

使用动态端口选择

避免硬编码端口。使用环境变量或自动查找可用端口:

const PORT = process.env.PORT || 3000;

// 或在绑定前检查端口可用性
const net = require('net');

function getAvailablePort(startPort) {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.listen(startPort, () => {
      const port = server.address().port;
      server.close(() => resolve(port));
    });
    server.on('error', () => {
      getAvailablePort(startPort + 1).then(resolve);
    });
  });
}

以编程方式处理错误

捕获错误并做出适当响应:

server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} is already in use`);
    // 尝试另一个端口或优雅退出
    process.exit(1);
  }
});

常见场景和快速修复

Nodemon 未释放端口

如果使用 nodemon,请确保使用 Ctrl+C 停止它,而不是 Ctrl+Z。后者会挂起进程而不释放端口。

Docker 容器冲突

检查正在运行的容器:

docker ps
docker stop <container_id>

VS Code 终端问题

在重启服务器之前关闭所有集成终端。有时 VS Code 会在后台保持进程处于活动状态。

代码中的多个服务器实例

确保在代码库中没有多次调用 app.listen()。这个常见错误会立即触发地址已被使用错误。

结论

Node.js EADDRINUSE 错误虽然令人沮丧,但很容易修复。使用 lsofnetstat 找到使用你端口的进程,终止它,并实现适当的关闭处理程序以防止未来再次发生。记住:使用信号处理程序的优雅关闭和动态端口选择将使你免于反复处理这个 Node.js 服务器端口错误

常见问题

可以,你可以通过捕获 EADDRINUSE 错误并尝试下一个可用端口来实现自动端口切换。使用递归函数或循环按顺序测试端口,直到找到一个开放的端口,或使用像 portfinder 这样自动处理此操作的库。

关闭终端窗口并不总是终止正在运行的进程。后台进程,特别是那些使用 nohup 启动或作为服务运行的进程,会继续运行。始终使用适当的终止命令,如 Ctrl+C 或使用 PID 显式终止进程,以确保端口被释放。

使用 kill -9 会强制立即终止而不允许清理,这可能导致数据丢失或损坏。始终先尝试 kill -15 以实现优雅关闭。将 kill -9 保留给无响应的进程。在代码中实现适当的信号处理程序以确保干净的关闭。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay