WebSocket连接频繁断开与重连的逻辑处理

WebSocket连接频繁断开与重连的逻辑处理

📋 关键逻辑详解

指数退避重连:每次重连延迟加倍(baseDelay * 2^n),并设置上限(maxDelay),避免无限增长和网络风暴。

bG9pajNqLmNvbQ== # bv.uxhl7c.cn#gjasp?gsgjop-kk#asd

心跳保活:定期发送ping,若未收到pong则主动断开触发重连,及时清理“僵尸连接”。

bG9pajNqLmNvbQ== # gn.uxhl7c.cn#gjasp?gsgjop-kk#asd

状态管理:明确的连接状态(CONNECTING, CONNECTED, DISCONNECTED, CLOSED)是逻辑清晰的基石。

bG9pajNqLmNvbQ== # rx.uxhl7c.cn#gjasp?gsgjop-kk#asd

消息队列:在断开时缓存待发送消息,连接恢复后自动补发,防止数据丢失(注意:对于严格有序或过时无效的消息,需额外设计)。

bG9pajNqLmNvbQ== # wq.uxhl7c.cn#gjasp?gsgjop-kk#asd

连接健康检查:除了心跳,也可监听 onclose 事件,根据关闭码(如 1000 为正常关闭,1006 为异常断开)决定是否重连。

bG9pajNqLmNvbQ== # mv.uxhl7c.cn#gjasp?gsgjop-kk#asd

flowchart TD

A[“初始化 WebSocket 管理器”] --> B[“创建连接
(初始为CONNECTING状态)”]

B --> C{连接成功?}

C -- 是 --> D[“状态: CONNECTED
重置重连计数,开始心跳”]

D --> E{“运行期间...”}

E -- “收到消息” --> F[“处理业务消息,
重置心跳计时”]

E -- “心跳计时器超时” --> G[“主动断开,触发重连”]

C -- 否/网络错误 --> H[“状态: DISCONNECTED
进入重连逻辑”]

subgraph H [重连逻辑]

H1[“计算退避延迟
(指数退避 + 随机抖动)”]

H2[“延迟等待”]

H3[“尝试创建新连接”]

end

H3 --> I{“重连成功?”}

I -- 是 --> D

I -- 否 --> H

E -- “连接异常关闭/错误” --> H

E -- “手动调用 close()” --> J[“状态: CLOSED
清理资源,停止所有定时器”]

🛠️ 核心实现方案

这里提供一个封装了完整重连逻辑的 ReconnectingWebSocket 类,你可以直接使用或参考其思想。

bG9pajNqLmNvbQ== # lp.uxhl7c.cn#gjasp?gsgjop-kk#asd

class ReconnectingWebSocket {

constructor(url, protocols = []) {

this.url = url;

this.protocols = protocols;

this.ws = null;

// 状态管理

this.status = 'DISCONNECTED'; // 'CONNECTING', 'CONNECTED', 'DISCONNECTED', 'CLOSED'

// 重连控制

this.reconnectCount = 0;

this.maxReconnectAttempts = Infinity; // 最大重连次数,可设置

this.baseDelay = 1000; // 基础重连延迟(毫秒)

this.maxDelay = 30000; // 最大重连延迟

// 心跳控制

this.heartbeatInterval = 30000; // 心跳间隔

this.heartbeatTimer = null;

this.pongReceived = true; // 是否收到上一次心跳的回复

// 消息队列(在断开时缓存消息)

this.messageQueue = [];

this.connect();

}

connect() {

if (this.status === 'CONNECTED' || this.status === 'CONNECTING') {

return;

}

this.status = 'CONNECTING';

console.log(`[WebSocket] 连接中... 尝试次数: ${this.reconnectCount + 1}`);

try {

this.ws = new WebSocket(this.url, this.protocols);

this.setupEventHandlers();

} catch (error) {

console.error('[WebSocket] 创建连接失败:', error);

this.handleDisconnect();

}

}

setupEventHandlers() {

this.ws.onopen = () => {

console.log('[WebSocket] 连接已建立');

this.status = 'CONNECTED';

this.reconnectCount = 0; // 连接成功,重置重连计数

this.startHeartbeat();

// 连接建立后,发送积压的消息

this.flushMessageQueue();

};

this.ws.onmessage = (event) => {

// 收到任何消息都认为连接是活跃的,重置心跳标志

this.pongReceived = true;

// 在这里处理业务消息

this.handleMessage(event.data);

};

this.ws.onclose = (event) => {

console.log(`[WebSocket] 连接关闭,代码: ${event.code}, 原因: ${event.reason}`);

this.cleanup();

this.handleDisconnect();

};

this.ws.onerror = (error) => {

console.error('[WebSocket] 连接错误:', error);

// onerror 之后通常会触发 onclose,所以主要逻辑放在 onclose 里

this.ws.close(); // 确保触发 onclose

};

}

handleDisconnect() {

if (this.status === 'CLOSED') {

return; // 如果是手动关闭,则不重连

}

this.status = 'DISCONNECTED';

this.stopHeartbeat();

// 检查是否超过最大重连次数

if (this.reconnectCount >= this.maxReconnectAttempts) {

console.error(`[WebSocket] 已达最大重连次数 (${this.maxReconnectAttempts}),停止重连`);

return;

}

// 计算下一次重连的延迟(指数退避 + 随机抖动)

const delay = Math.min(

this.baseDelay * Math.pow(2, this.reconnectCount),

this.maxDelay

) + Math.random() * 1000; // 增加最多1秒的随机抖动,避免客户端同时重连

console.log(`[WebSocket] ${delay.toFixed(0)}ms 后尝试第 ${this.reconnectCount + 1} 次重连`);

setTimeout(() => {

this.reconnectCount++;

this.connect();

}, delay);

}

// 心跳机制

startHeartbeat() {

this.pongReceived = true;

if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);

this.heartbeatTimer = setInterval(() => {

if (this.ws && this.ws.readyState === WebSocket.OPEN) {

if (!this.pongReceived) {

console.warn('[WebSocket] 心跳无响应,主动断开连接');

this.ws.close(); // 触发重连逻辑

return;

}

this.pongReceived = false;

// 发送心跳ping(这里使用简单的文本,需与后端约定)

this.ws.send('__ping__');

}

}, this.heartbeatInterval);

}

stopHeartbeat() {

if (this.heartbeatTimer) {

clearInterval(this.heartbeatTimer);

this.heartbeatTimer = null;

}

}

// 消息处理

send(data) {

if (this.status === 'CONNECTED' && this.ws.readyState === WebSocket.OPEN) {

this.ws.send(data);

} else {

console.warn('[WebSocket] 连接未就绪,消息已加入队列:', data);

this.messageQueue.push(data); // 缓存消息,待连接恢复后发送

}

}

flushMessageQueue() {

while (this.messageQueue.length > 0) {

const message = this.messageQueue.shift();

this.ws.send(message);

}

}

handleMessage(data) {

// 过滤心跳回复

if (data === '__pong__') {

return;

}

// TODO: 在这里处理你的业务消息

console.log('[WebSocket] 收到消息:', data);

}

cleanup() {

this.stopHeartbeat();

if (this.ws) {

this.ws.onopen = null;

this.ws.onclose = null;

this.ws.onerror = null;

this.ws.onmessage = null;

this.ws = null;

}

}

// 手动关闭,不再重连

close() {

this.status = 'CLOSED';

if (this.ws) {

this.ws.close(1000, 'Manual closure');

}

this.cleanup();

console.log('[WebSocket] 连接已手动关闭');

}

}

// 使用示例

const wsClient = new ReconnectingWebSocket('wss://your-server.com/ws');

wsClient.send(JSON.stringify({ type: 'hello' }));

🚀 生产环境进阶考虑

网络状态感知:可结合 navigator.onLine API,在离线时暂停重连尝试。

bG9pajNqLmNvbQ== # xf.uxhl7c.cn#gjasp?gsgjop-kk#asd

认证重连:如果连接需要认证,重连时可能需要重新携带Token,可在 onopen 中发送认证消息。

bG9pajNqLmNvbQ== # ia.uxhl7c.cn#gjasp?gsgjop-kk#asd

重连成功回调:可暴露 onReconnected 事件,通知应用层恢复数据同步。

bG9pajNqLmNvbQ== # vz.uxhl7c.cn#gjasp?gsgjop-kk#asd

监控与日志:将重连次数、断开原因上报到监控系统,便于分析稳定性。

bG9pajNqLmNvbQ== # ev.uxhl7c.cn#gjasp?gsgjop-kk#asd

相关推荐

阴房阗鬼火,春院閟天黑。
365足球体育网站

阴房阗鬼火,春院閟天黑。

📅 08-01 👁️ 858
Norman的用法、是什么意思、怎么读、翻译
beat365上不去

Norman的用法、是什么意思、怎么读、翻译

📅 09-29 👁️ 5204
在 iPhone 上获取音乐
365足球体育网站

在 iPhone 上获取音乐

📅 07-19 👁️ 5702
jQuery下载和安装教程
365足球体育网站

jQuery下载和安装教程

📅 10-16 👁️ 4199
如何在安卓手机上实现微信双开:四种实用方法详解
365足球体育网站

如何在安卓手机上实现微信双开:四种实用方法详解

📅 09-17 👁️ 7711
勐的解释
beat365上不去

勐的解释

📅 09-29 👁️ 7432