如何实现具备自动重连与心跳检测的WebSocket客户端
目录
如何实现具备自动重连与心跳检测的WebSocket客户端
本文介绍如何通过原生WebSocket API封装一个具备自动重连、心跳检测、错误恢复等能力的稳健客户端。适用于需要长连接的实时通讯场景(如聊天室、实时数据监控等)。
核心功能亮点
- 自动重连机制 - 指数退避策略重连
- 心跳保活 - 双向检测连接活性
- 消息可靠性 - 失败消息自动重发
- 异常处理 - 错误分类处理机制
- 状态管理 - 精准控制连接生命周期
关键优化点说明
- 事件监听优化
- 改用
addEventListener
替代onopen
等属性赋值,允许多监听器共存 - 新增错误边界处理,防止初始化失败导致程序崩溃
- 智能重连策略
- 避免网络恢复后仍要等待过长的延迟时间
- 状态管理改进
- 通过属性访问器清晰管理连接状态
- 消息可靠性增强
- 独立队列存储未发送消息
- 连接恢复时自动重发
- 配置与状态分离
- 独立config对象管理配置参数
- state对象清晰反映运行时状态
- /**
- WebSocket客户端实现
- @param {string} url - 连接地址
- @param {string|string[]} [protocols] - 可选协议
/
class WebSocketClient {
// 默认配置(可通过构造函数覆盖)
static config = {
HEARTBEAT_INTERVAL: 30000, // 心跳间隔30s
HEARTBEAT_TIMEOUT: 10000, // 心跳超时10s
MAX_RECONNECT_ATTEMPTS: 5, // 最大重连次数
BASE_RECONNECT_DELAY: 1000, // 基础重连延迟
MAX_RECONNECT_DELAY: 30000, // 最大重连延迟30s
MAX_PENDING_MESSAGES: 50 // 最大消息队列长度
};
// 运行时状态
state = {
ws: null,
reconnectCount: 0, //重新连接计数
isUserClosed: false, //用户自己关闭
pendingMessages: [], //待发送的消息队列
heartbeatTimer: null, //心跳计时器
heartbeatTimeoutTimer: null //消息超时计时器
};
constructor(url, protocols, options = {}) {
// 合并配置
this.config = {
…WebSocketClient.config,
…options
};
this.url = url;
this.protocols = protocols;
this.connect();
}
/* 初始化连接 /
connect() {
this.dispose(); // 清理旧连接
try {
// 创建WebSocket实例
this.state.ws = this.protocols ?
new WebSocket(this.url, this.protocols) :
new WebSocket(this.url);
this.bindEventListeners();
} catch (error) {
console.error(‘WebSocket初始化失败:’, error);
this.handleReconnect();
}
}
/* 清理旧连接和定时器 /
dispose() {
if (this.state.ws) {
this.state.ws.close();
this.state.ws = null;
}
this.clearHeartbeat();
}
/* 绑定事件监听器 /
bindEventListeners() {
const {
ws
} = this.state;
ws.addEventListener(‘open’, (event) => {
console.log(‘连接已建立’, event);
/* 重置重连状态 /
this.resetReconnect();
//开始心跳
this.startHeartbeat();
/* 发送队列中的消息 /
this.flushPendingMessages();
});
ws.addEventListener(’error’, (error) => {
console.error(‘连接异常:’, error);
this.handleReconnect();
});
ws.addEventListener(‘close’, (event) => {
console.log(‘连接关闭:’, event);
this.handleClose(event);
});
ws.addEventListener(‘message’, (event) => {
this.handleIncomingMessage(event);
});
}
/* 心跳管理 /
startHeartbeat() {
this.clearHeartbeat();
this.state.heartbeatTimer = setInterval(() => {
if (this.isConnected) {
this.sendHeartbeat();
this.monitorHeartbeatResponse();
}
}, this.config.HEARTBEAT_INTERVAL);
}
/* 发送心跳包 /
sendHeartbeat() {
this.send(JSON.stringify({
type: ‘ping’,
timestamp: Date.now()
}));
}
/* 监测心跳响应 /
monitorHeartbeatResponse() {
this.state.heartbeatTimeoutTimer = setTimeout(() => {
console.warn(‘心跳响应超时,执行断开’);
this.state.ws.close();
}, this.config.HEARTBEAT_TIMEOUT);
}
/* 处理接收消息 /
handleIncomingMessage(event) {
try {
const data = JSON.parse(event.data);
if (data.type === ‘ping’) {
//关闭超时计时器
clearTimeout(this.state.heartbeatTimeoutTimer);
return;
}
this.onMessage?.(data);
} catch (error) {
console.error(‘消息解析失败:’, error);
this.onError?.(error);
}
}
/* 重连控制 /
handleReconnect() {
if (this.shouldReconnect) {
const delay = this.calculateReconnectDelay();
console.log(
将在 ${delay}ms 后重试...
); setTimeout(() => { this.state.reconnectCount++; this.connect(); }, delay); } } /* 智能重连延迟计算 / calculateReconnectDelay() { return Math.min( this.config.BASE_RECONNECT_DELAY * (2 ** this.state.reconnectCount), this.config.MAX_RECONNECT_DELAY ); } /* 发送消息(带队列控制) / send(data) { if (this.isConnected) { this.state.ws.send(data); } else { if (this.state.pendingMessages.length >= this.config.MAX_PENDING_MESSAGES) { console.warn(‘消息队列已满,丢弃最旧消息’); this.state.pendingMessages.shift(); } this.state.pendingMessages.push(data); } } /* 清空心跳定时器 / clearHeartbeat() { clearInterval(this.state.heartbeatTimer); clearTimeout(this.state.heartbeatTimeoutTimer); this.state.heartbeatTimer = null; this.state.heartbeatTimeoutTimer = null; } /* 重置重连状态 / resetReconnect() { this.state.reconnectCount = 0; this.state.isUserClosed = false; } /* 处理连接关闭事件 / handleClose(event) { this.clearHeartbeat(); if (!this.state.isUserClosed) { this.handleReconnect(); } } /* 发送队列中的消息 */ flushPendingMessages() { while (this.state.pendingMessages.length > 0 && this.isConnected) { this.state.ws.send(this.state.pendingMessages.shift()); } } // 公开方法 //关闭 close() { this.state.isUserClosed = true; this.dispose(); } //重链接 reconnect() { this.close(); this.state.isUserClosed = false; this.connect(); } // 状态判断 //判断socket是开启状态吗 get isConnected() { return this.state.ws?.readyState === WebSocket.OPEN; } //判断可以重连吗 get shouldReconnect() { return !this.state.isUserClosed && this.state.reconnectCount < this.config.MAX_RECONNECT_ATTEMPTS; } } // 使用示例 // const ws = new WebSocketClient(‘wss://echo.websocket.org’); // ws.onMessage = (data) => console.log(‘收到消息:’, data); // ws.send(‘Hello World’);
使用示例
// 初始化客户端 const client = new WebSocketClient(‘wss://api.example.com/ws’); // 监听消息 client.onMessage = (data) => { console.log(‘收到消息:’, data); }; // 发送消息 document.getElementById(‘sendBtn’).addEventListener(‘click’, () => { client.send(JSON.stringify({ type: ‘chat’, content: ‘Hello World’ })); }); // 异常处理 client.onError = (error) => { console.error(‘连接异常:’, error); };