ReconnectingWebSocket类通过状态管理、指数退避重试、消息队列缓冲实现生产级自动重连:检测断连、限制重试次数、缓存未发消息、兼容原生事件接口,并支持手动关闭与暂停。

WebSocket 自动重连不是靠单次 connect() 调用就能解决的,关键在于状态管理、重试策略和连接生命周期的封装。下面是一个轻量、可复用、生产可用的自动重连 WebSocket 类实现思路和代码。
核心设计原则
自动重连类必须能:
- 检测连接断开(包括网络中断、服务端关闭、心跳超时)
- 避免无限重连或密集重试(需退避策略)
- 保证消息发送队列在重连期间不丢失
- 提供统一的 onopen/onmessage/onclose/onerror 接口,兼容原生习惯
- 支持手动关闭与暂停重连
带退避重试的连接管理
每次重连不应固定间隔,而应采用指数退避(如 1s → 2s → 4s → 8s → 最大 30s),并限制最大重试次数(例如 10 次)。连接失败后清空当前 socket 引用,延时后新建实例:
- 用
setTimeout控制重试延迟,避免阻塞 - 记录已重试次数,达到上限后触发
onreconnectfail回调 - 每次重连前检查
this.shouldReconnect === true,支持手动暂停
消息发送缓冲与重发机制
连接未就绪(readyState !== 1)时,不能丢弃消息。应维护一个待发队列:
- 调用
send(data)时,若未连接,将数据推入this.pendingQueue - 连接成功(
onopen)后,遍历队列逐条发送 - 可选:对关键消息加唯一 ID + 服务端 ACK,实现可靠投递(需协议配合)
完整可运行类示例
以下是一个精简但完整的实现(ES6 Class,无依赖):
立即学习“Java免费学习笔记(深入)”;
class ReconnectingWebSocket {
constructor(url, protocols = []) {
this.url = url;
this.protocols = protocols;
this.ws = null;
this.readyState = WebSocket.CONNECTING;
this.shouldReconnect = true;
this.reconnectCount = 0;
this.maxReconnect = 10;
this.reconnectDelay = 1000; // 初始延迟 1s
this.pendingQueue = [];
this.eventHandlers = { open: [], message: [], close: [], error: [], reconnectfail: [] };
<pre class='brush:php;toolbar:false;'>this.connect();}
connect() { if (this.ws && [WebSocket.OPEN, WebSocket.CONNECTING].includes(this.ws.readyState)) return;
this.ws = new WebSocket(this.url, this.protocols);
this.readyState = WebSocket.CONNECTING;
this.ws.onopen = () => {
this.readyState = WebSocket.OPEN;
this.reconnectCount = 0;
this.reconnectDelay = 1000;
this.flushPending();
this.trigger('open');
};
this.ws.onmessage = (e) => this.trigger('message', e);
this.ws.onclose = (e) => {
this.readyState = WebSocket.CLOSED;
this.trigger('close', e);
if (this.shouldReconnect && this.reconnectCount < this.maxReconnect) {
this.reconnectCount++;
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000);
setTimeout(() => this.connect(), this.reconnectDelay);
} else if (this.reconnectCount >= this.maxReconnect) {
this.trigger('reconnectfail');
}
};
this.ws.onerror = (e) => {
this.trigger('error', e);
// onerror 不代表连接已断,但常伴随 onclose,此处不主动重连,交由 onclose 处理
};}
send(data) { if (this.readyState === WebSocket.OPEN && this.ws) { this.ws.send(data); } else { this.pendingQueue.push(data); } }
flushPending() { while (this.pendingQueue.length > 0) { const data = this.pendingQueue.shift(); try { this.ws.send(data); } catch (e) { // 发送失败,放回队首或丢弃(按需) this.pendingQueue.unshift(data); break; } } }
close(code = 1000, reason = '') { this.shouldReconnect = false; if (this.ws) this.ws.close(code, reason); }
on(event, handler) { if (this.eventHandlers[event]) { this.eventHandlers[event].push(handler); } }
trigger(event, ...args) { const handlers = this.eventHandlers[event] || []; handlers.forEach(fn => fn(...args)); } }
使用方式与原生一致:
const ws = new ReconnectingWebSocket('wss://echo.websocket.org');
<p>ws.on('open', () => console.log('connected'));
ws.on('message', e => console.log('recv:', e.data));
ws.on('reconnectfail', () => console.error('give up reconnecting'));</p><p>ws.send('hello'); // 自动排队或直发</p>不复杂但容易忽略:务必在页面卸载前调用 ws.close(),防止后台静默重连;若需心跳保活,可在 onopen 后启动 setInterval 定期发送 ping 消息,并监听 pong 响应做超时判定。


















