Nodejs eaddrinuse谁知道是什么原因造成的吗?

Nodejs eaddrinuse谁知道是什么原因造成的吗?

今天child_process.fork创建一个子进程来做session服务器, 然后web服务器用socket跟session服务器交换数据, 问题是我用ab压web服务器的时候(winxp环境下apache ab -n 1000 -c 1000 http://xxx) 经常提示eaddrinuse 我很奇怪 这句话翻译不是"错误:地址使用中"么? 我只是客户端连接, 又不是监听端口 怎么会提示这个错误呢 奇怪…

2 回复

当你在使用 Node.js 的 child_process.fork 创建子进程时,可能会遇到 EADDRINUSE 错误。这个错误通常表示你尝试绑定到已经被占用的网络端口。即使你认为自己只是作为客户端连接,也有可能在某些情况下遇到这个问题。

原因

EADDRINUSE 错误通常发生在以下几种情况:

  1. 端口被其他进程占用:如果你尝试在一个已经被其他进程占用的端口上进行监听,就会触发这个错误。
  2. 多次调用同一端口:在同一进程中多次调用 server.listen(port) 而没有正确地关闭服务器。
  3. 进程未正确退出:之前的进程可能没有完全退出,导致端口仍然被占用。

示例代码

假设你有一个简单的 Web 服务器和一个 session 服务器,都试图监听同一个端口(例如 8080):

// web-server.js
const http = require('http');
const { fork } = require('child_process');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
});

server.listen(8080, () => {
    console.log('Web server listening on port 8080');
});

const child = fork('./session-server.js');
// session-server.js
const net = require('net');

const server = net.createServer((socket) => {
    socket.write('Echo server\r\n');
    socket.pipe(socket);
});

server.listen(8080, () => {
    console.log('Session server listening on port 8080');
});

在这个例子中,web-server.jssession-server.js 都试图监听同一个端口(8080),这会导致 EADDRINUSE 错误。

解决方法

  1. 检查端口是否被占用:可以使用命令行工具如 lsof -i :8080netstat -an | grep 8080 来查看哪个进程占用了该端口。
  2. 更改监听端口:确保每个服务器监听不同的端口。
  3. 确保进程正确退出:确保所有子进程和主进程都能正确退出,释放端口。

修改后的代码

// web-server.js
const http = require('http');
const { fork } = require('child_process');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
});

server.listen(8081, () => { // 更改端口
    console.log('Web server listening on port 8081');
});

const child = fork('./session-server.js');
// session-server.js
const net = require('net');

const server = net.createServer((socket) => {
    socket.write('Echo server\r\n');
    socket.pipe(socket);
});

server.listen(8080, () => {
    console.log('Session server listening on port 8080');
});

通过更改端口,避免了端口冲突,从而解决了 EADDRINUSE 错误。


EADDRINUSE 错误表示尝试绑定到已经被占用的地址和端口。即使你只是客户端连接,但如果你在创建 Socket 时指定的地址和端口已经被其他进程或同一程序中的其他实例占用,也会遇到这个错误。

这种情况可能发生在你使用 child_process.fork 创建的子进程中,如果子进程在退出后没有正确地释放资源(如未关闭的 socket),那么在下次启动相同子进程时可能会尝试绑定到已经被占用的地址和端口。

示例代码

假设你在主进程中创建了一个子进程,并且子进程中有一个用于与 session 服务器通信的 socket:

const { fork } = require('child_process');
const net = require('net');

// 主进程
const child = fork('./sessionServer.js'); // 子进程

// 定义客户端 socket
const clientSocket = new net.Socket();
clientSocket.connect(3000, 'localhost', () => {
    console.log('Connected to session server');
});

// 处理错误事件
clientSocket.on('error', (err) => {
    if (err.code === 'EADDRINUSE') {
        console.error('Error: Address already in use');
        // 在这里可以尝试重启子进程或者等待一段时间后重试
    }
});

sessionServer.js 中定义子进程的逻辑:

const net = require('net');

// 创建一个服务器
const server = net.createServer((socket) => {
    socket.write('Welcome to the session server\n');
});

server.listen(3000, 'localhost', () => {
    console.log('Session server listening on port 3000');
});

// 处理服务器关闭时的情况
process.on('SIGINT', () => {
    server.close(() => {
        console.log('Server closed');
        process.exit(0);
    });
});

解决方案

  1. 确保子进程正确退出:在子进程接收到 SIGINT 信号时,确保关闭所有打开的 socket。
  2. 增加超时处理:在客户端 socket 连接失败时,可以设置一个定时器,过一段时间后尝试重新连接。
  3. 检查端口是否被占用:在启动子进程之前,检查所需端口是否已经被占用。

以上是基本的排查思路和解决方案。

回到顶部