连接WebSocket服务器出现Error: connect ECONNREFUSED错误的解决
问题
我搭建了一个OSC服务器,负责接收消息并转发给其他客户端,其中一个客户端是控制计时器的WebSocket服务器。以下是实现脚本:
const osc = require('osc'); const WebSocket = require('ws'); let ws = undefined; // web socket, timer function webSocket() { ws = new WebSocket('ws:192.168.1.118:4000'); ws.on('open', function open() { console.log('WebSocket connected'); }); ws.on('close', function close() { console.log('WebSocket disconnected, attempting to reconnect...'); setTimeout(webSocket, 1000); }); ws.on('error', function error(err) { console.error('WebSocket encountered an error: ', err.message, 'Closing socket'); ws.close(); }); } webSocket(); var udp = new osc.UDPPort({ localAddress: "0.0.0.0", localPort: 8000, }); udp.on('ready', function () { console.log('OSC Server is listening'); console.log(JSON.stringify(udp)); }); // Receive osc message udp.on('message', function (msg, timeTage, info) { console.log('An OSC message received: ', msg); // Send exact message to movie player program if(info.address !== '10.4.0.95') { udp.send(msg, '10.4.0.95', 9004); } // Send message to tablet if (msg.address.startsWith('/mov/moviePlayhead') && info.address !== '10.4.0.94') { udp.send(msg, '10.4.0.94', 9003) } // Send message to web socket, timer if (msg.address === '/bank/1/trig/3') { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({event:'kaleidoscope', action: 'play', type: '3'})); } } else if (msg.address === '/bank/1/trig/2') { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({event:'kaleidoscope', action: 'play', type: '5'})); } } else if (msg.address === '/bank/1/trig/1') { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({event:'kaleidoscope', action: 'play', type: '7'})); } } }); udp.open();
脚本多数时候运行正常,但偶尔会出现如下错误:
Error: connect ECONNREFUSED 192.168.1.118:4000 at TCPConnectWrap.afterConnect [as oncomplete] { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '192.168.1.118', port 4000 }
出现错误时,192.168.1.118:4000的WebSocket服务器是正常运行的,目前只能通过重启OSC服务器解决,但希望避免这种操作。我已经添加了重连逻辑,但并未生效,请问问题出在哪里?如何实现有效的重连?
问题分析与解决方案
问题根源
- 连接阶段错误未触发重连:当WebSocket在连接建立前出现
ECONNREFUSED错误时,此时ws实例还未进入open状态,调用ws.close()不会触发close事件,导致重连逻辑无法执行。 - 无效的ws实例引用:重连时直接覆盖全局
ws变量,但旧实例的事件监听可能残留,导致状态判断混乱。 - 重连策略单一:固定1秒重试间隔,未考虑网络波动的情况,可能导致短时间内大量重试,反而加剧问题。
修复方案
1. 调整错误事件处理逻辑
连接阶段的错误无需调用close(),直接触发重连;仅在连接建立后的错误才执行关闭并重连。
2. 增加连接超时机制
避免因网络延迟导致的长时间等待,超时后主动触发重连。
3. 优化重连策略
使用指数退避(每次重试间隔翻倍,直到最大间隔),减少无效重试频率。
4. 严谨判断ws状态
发送消息时,不仅要检查readyState,还要确保当前ws实例是最新的。
修改后的完整代码
const osc = require('osc'); const WebSocket = require('ws'); let ws = undefined; let reconnectInterval = 1000; // 初始重连间隔 const maxReconnectInterval = 30000; // 最大重连间隔 function webSocket() { // 创建新实例前,先销毁旧实例的事件监听 if (ws) { ws.removeAllListeners(); ws = undefined; } ws = new WebSocket('ws://192.168.1.118:4000'); // 补全ws协议前缀 // 连接超时处理:3秒未连接成功则触发重连 const timeout = setTimeout(() => { console.log('WebSocket connection timed out'); ws.close(); }, 3000); ws.on('open', function open() { console.log('WebSocket connected'); reconnectInterval = 1000; // 连接成功后重置重连间隔 clearTimeout(timeout); }); ws.on('close', function close(code, reason) { console.log(`WebSocket disconnected (code: ${code}, reason: ${reason}), attempting to reconnect...`); // 指数退避计算下一次重连间隔 reconnectInterval = Math.min(reconnectInterval * 2, maxReconnectInterval); setTimeout(webSocket, reconnectInterval); }); ws.on('error', function error(err) { console.error('WebSocket encountered an error: ', err.message); // 连接阶段的错误直接触发重连,无需调用close if (ws.readyState === WebSocket.CONNECTING) { clearTimeout(timeout); setTimeout(webSocket, reconnectInterval); } else { // 已连接后的错误,关闭后触发close事件重连 ws.close(); } }); } webSocket(); const udp = new osc.UDPPort({ localAddress: "0.0.0.0", localPort: 8000, }); udp.on('ready', function () { console.log('OSC Server is listening'); }); // Receive osc message udp.on('message', function (msg, timeTage, info) { console.log('An OSC message received: ', msg); // Send exact message to movie player program if(info.address !== '10.4.0.95') { udp.send(msg, '10.4.0.95', 9004); } // Send message to tablet if (msg.address.startsWith('/mov/moviePlayhead') && info.address !== '10.4.0.94') { udp.send(msg, '10.4.0.94', 9003); } // Send message to web socket, timer const sendWsMsg = (type) => { // 确保ws实例存在且处于OPEN状态 if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({event:'kaleidoscope', action: 'play', type})); } else { console.log('WebSocket not connected, message dropped:', type); } }; switch(msg.address) { case '/bank/1/trig/3': sendWsMsg('3'); break; case '/bank/1/trig/2': sendWsMsg('5'); break; case '/bank/1/trig/1': sendWsMsg('7'); break; } }); udp.open();
额外说明
- 补全了WebSocket URL的协议前缀
ws://,避免部分环境下的解析问题。 - 使用
switch语句替代多个if-else,提升代码可读性。 - 添加了消息发送失败的日志,便于排查问题。
内容的提问来源于stack exchange,提问作者ksatione
相关产品推荐
相关产品推荐

