WebSocket、断线重连与消息队列
WebSocket 介绍
Websocket 是一种在单个 TCP 连接上提供全双工通信的协议。它由 HTML5 引入,旨在代替传统的轮询或长轮询方式,实现更高效的实时数据传输。
工作流程:
- 握手阶段:使用 HTTP 协议进行初始握手,随后将协议升级为 WebSocket 协议。
- 客户端发送 Upgrade 请求头
- 服务器响应 101 Switching Protocols 状态码,完成协议切换。
- 数据传输阶段:客户端和服务器可以任意方向发送消息,无需再通过 HTTP 请求头部信息,降低了通信开销。
特点:
- 全双工通信:客户端和服务器可以实时地相互发送消息。
- 低延迟:相比 HTTP 轮询,WebSocket 避免了频繁建立和断开连接,显著降低了延迟。
- 长连接:一次连接建立后可持续使用,减少了网络资源的消耗。
js
class WebSocketManager {
constructor(url, maxRetries = 5) {
this.url = url;
this.socket = null;
this.messageQueue = []; // 消息队列
this.isConnected = false;
this.reconnectAttempts = 0; // 当前重连次数
this.maxRetries = maxRetries; // 最大重连次数
this.init();
}
// 初始化
init() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket connected!');
this.isConnected = true;
this.reconnectAttempts = 0; // 重连成功后重置计数
this.flushQueue(); // 重连后发送消息队列中的消息
};
this.socket.onclose = () => {
console.warn('WebSocket disconnect!');
this.isConnected = false;
this.reconnect(); // 尝试重连
};
}
reconnect() {
if (this.reconnectAttempts >= this.maxRetries) {
console.error(
'Max reconnect attempts reached. Please check your network.'
);
return; // 达到最大重连次数后停止重连
}
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000); // 指数退避,最大 30 秒
this.reconnectAttempts++;
console.log(
`Reconnecting WebSocket in ${delay / 1000} seconds... (Attempt ${
this.reconnectAttempts
}/${this.maxRetries})`
);
setTimeout(() => {
this.init(); // 尝试重新初始化 WebSocket
}, delay);
}
// 发送消息
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
try {
this.socket.send(message);
console.log('Message sent:', message);
} catch (error) {
console.error('Error sending message:', error);
this.messageQueue.push(message);
}
} else {
console.warn('Connection lost, message queued:', message);
this.messageQueue.push(message); // 入队
}
}
// 清空队列
flushQueue() {
while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift(); // 从队列中取出消息
this.sendMessage(message); // 重发消息
}
}
}
// 使用示例
const wsManager = new WebSocketManager('wss://echo.websocket.org', 3);
// 模拟发送消息
wsManager.sendMessage('Hello, Server!');
wsManager.sendMessage('Another message');
// 模拟断线后重连,未发送的消息会在连接恢复后自动重发
setTimeout(async () => {
await wsManager.socket.close();
wsManager.sendMessage('after close');
}, 3000);