Files
pqs-9100_client/scripts/port-checker.js
2025-10-16 20:18:20 +08:00

155 lines
4.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const net = require('net');
/**
* 端口检测工具
*/
class PortChecker {
/**
* 检查端口是否可用检测0.0.0.0,确保能绑定到所有地址)
* @param {number} port - 端口号
* @param {string} host - 主机地址,默认 0.0.0.0(所有地址)
* @returns {Promise<boolean>} true 表示端口可用false 表示已被占用
*/
static checkPort(port, host = '0.0.0.0') {
return new Promise((resolve) => {
// 先尝试连接,看是否有服务在监听
const testSocket = new net.Socket();
testSocket.setTimeout(200);
testSocket.on('connect', () => {
// 能连接上,说明端口被占用
console.log(`[PortChecker] Port ${port} is in use (connection successful)`);
testSocket.destroy();
resolve(false);
});
testSocket.on('timeout', () => {
// 超时,再用绑定方式检测
testSocket.destroy();
this._checkPortByBinding(port, host, resolve);
});
testSocket.on('error', (err) => {
testSocket.destroy();
if (err.code === 'ECONNREFUSED') {
// 连接被拒绝,说明没有服务监听,再用绑定方式确认
this._checkPortByBinding(port, host, resolve);
} else {
// 其他错误,认为端口可用
resolve(true);
}
});
testSocket.connect(port, '127.0.0.1');
});
}
static _checkPortByBinding(port, host, resolve) {
const server = net.createServer();
server.once('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.log(`[PortChecker] Port ${port} is in use (EADDRINUSE)`);
resolve(false);
} else {
console.log(`[PortChecker] Port ${port} check error: ${err.code}`);
resolve(false);
}
});
server.once('listening', () => {
server.close();
console.log(`[PortChecker] Port ${port} is available`);
resolve(true);
});
server.listen(port, host);
}
/**
* 查找可用端口(从指定端口开始递增查找)
* @param {number} startPort - 起始端口
* @param {number} maxAttempts - 最大尝试次数默认100
* @param {string} host - 主机地址,默认 0.0.0.0
* @returns {Promise<number>} 返回可用的端口号,如果都不可用则返回 -1
*/
static async findAvailablePort(startPort, maxAttempts = 100, host = '0.0.0.0') {
console.log(`[PortChecker] Searching for available port starting from ${startPort}...`);
for (let i = 0; i < maxAttempts; i++) {
const port = startPort + i;
const isAvailable = await this.checkPort(port, host);
if (isAvailable) {
console.log(`[PortChecker] ✓ Found available port: ${port}`);
return port;
} else {
console.log(`[PortChecker] ✗ Port ${port} is in use, trying ${port + 1}...`);
}
}
console.error(`[PortChecker] ✗ No available port found from ${startPort} to ${startPort + maxAttempts - 1}`);
return -1;
}
/**
* 等待端口开始监听(用于检测服务是否启动成功)
* @param {number} port - 端口号
* @param {number} timeout - 超时时间毫秒默认30秒
* @param {string} host - 主机地址
* @returns {Promise<boolean>} true 表示端口已开始监听
*/
static async waitForPort(port, timeout = 30000, host = '127.0.0.1') {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const isListening = await this.isPortListening(port, host);
if (isListening) {
console.log(`[PortChecker] Port ${port} is now listening`);
return true;
}
// 等待500ms后重试
await new Promise(resolve => setTimeout(resolve, 500));
}
console.error(`[PortChecker] Timeout waiting for port ${port} to listen`);
return false;
}
/**
* 检查端口是否正在监听
* @param {number} port - 端口号
* @param {string} host - 主机地址
* @returns {Promise<boolean>}
*/
static isPortListening(port, host = '127.0.0.1') {
return new Promise((resolve) => {
const socket = new net.Socket();
socket.setTimeout(1000);
socket.once('connect', () => {
socket.destroy();
resolve(true);
});
socket.once('timeout', () => {
socket.destroy();
resolve(false);
});
socket.once('error', () => {
socket.destroy();
resolve(false);
});
socket.connect(port, host);
});
}
}
module.exports = PortChecker;