websocket onclose方法什么时候触发_WebSocket断开重连解决方案,心跳重连实践

bb2335ffad73526b08d685e7caf0aab9.png

    WebSocket是前后端交互的长连接,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。项目中,我们经常会使用WebSocket和服务器建立持久的连接。

    但是前后端也会因为某些不明因素链接断开(我就是因为经常断网38ef39795caee3ffd92a43f946bab5e2.png),导致前后端都没有反馈的情况

    e5793ad49711612151f588fb257ff8c6.png

所以为了保持链接的稳定性和持续性,心跳重连就必须整上去了035a34c479add2bef160e6c5fec99d16.png035a34c479add2bef160e6c5fec99d16.png035a34c479add2bef160e6c5fec99d16.png,也顺便记录一下实现心跳重连的流程

    在使用WebSocket的时候,如果网络突然断开,WebSocketd是不会触发任何事件的,所以前端程序无法得知当前链接是否断开。但是这个时候使用WebSocket.send方法的时候,浏览器会发现消息发不出去,隔一段时间之后(貌似每个浏览器隔的时间不相同),会触发onclose函数。利用这点9775052bbe25aa2aaae875bd3995e3b6.png,我们可以在send不出消息并触发onclose之后,进行重连

    当然后端也可能出现异常,他收到了这个消息,但是没响应回来,前端也收不到通知,差不多就是你给一个人打电话,那niao人不说话的情况,问题你还不知道他在不在。所以这种情况我们要隔段时间send一次,假如超过一定时间没收到回应,那就默认是异常了,就触发重连。

WebSocket的各个绑定事件:

let ws = new WebSocket(url);ws.onopen = function () {   //something}ws.onmessage = function (event) {   //something}ws.onclose = function () {    //something}ws.onerror = function () {    //something}

好了,按照这个思路,开始:

// 心跳检测let heartCheck = {    timeout: 60000, // 超时时间    timer: null,    serverTimer: null,    reset: function(){        clearTimeout(this.timer);     this.start();    },    start: function(){        this.timer = setTimeout(function(){            ws.send('connectTest');        }, this.timeout)    }}

定义了一个心跳检测,当open的时候,执行heartCheck.start()方法,然后onmessage收到信息之后调用heartCheck.reset()方法重置,这样每次onmessage就触发send,达到循环发送的效果。

当send失败的时候,隔一段时间会自动触发onclose,所以要在onclose的时候调用重连

ws.onclose = function () {    console.log('onclose');    reconnect();}

重连的时候需要注意防止重复连接,还要设置延迟,避免请求太频繁

let lockReconnect  = false;/** * @method reconnect ws重新连接 * @description lockReconnect防止重复连接,时间戳避免在失败时候会频繁建立ws连接 */ function reconnect() {    if(lockReconnect) return;    lockReconnect = true;    //没连接上会一直重连,设置延迟避免请求过多    setTimeout(function () {        createWebSocket(); // 创建webSocket连接的方法        lockReconnect = false;    }, 2000);}

如此上面流程就解决了如断网send不出消息的时候重连的效果,测试的时候可以手动断网测,亲测有效a6e31c759ee3c2eb67e05bedc3038df0.png

好了,现在假设后端异常,没数据返回,onmessage就进不去,得另外想办法。所以在每次send的时候的setTimeout内再加一个setTimeout,就是,如果里面这个setTimeout执行了,那就不等了,我觉得他是挂了,重连。

// 心跳检测let heartCheck = {    timeout: 60000, // 超时时间    timer: null,    serverTimer: null,    reset: function(){        clearTimeout(this.timer);        clearTimeout(this.serverTimer);     this.start();    },    start: function(){        let ts = this;        this.timer = setTimeout(function(){            ws.send('connectTest');            // 超出时间内未响应就主动关闭链接,关闭链接会触发重连            ts.serverTimer = setTimeout(function(){                ws.onclose();            }, ts.timeout)                    }, this.timeout)    }}

如果onmessage收到消息,执行了reset会清空所有的timer,重新计时, nice~~~。

这样就完成了websocket的心跳重连,归纳一下代码:

let lockReconnect = false; //避免重复连接let ws;// 心跳检测let heartCheck = {    timeout: 60000,    timer: null,    serverTimer: null,    reset: function(){        clearTimeout(this.timer);        clearTimeout(this.serverTimer);     this.start();    },    start: function(){        let ts = this;        this.timer = setTimeout(function(){            ws.send('connectTest');            ts.serverTimer = setTimeout(function(){                ws.onclose();            }, ts.timeout)        }, this.timeout)    }}// 创建WebSocket链接function createWebSocket () {    if ("WebSocket" in window) {        if (!url) return        ws = new WebSocket(url);                // WebSocket事件方法        initEventHandle();    } else {        console.log('您的浏览器不支持websocket')    }}/** * @method initEventHandle 初始化ws各个事件的方法 */function initEventHandle (url) {    ws.onopen = function () {        heartCheck.start();        console.log('链接成功:', url);    }    //获得消息事件    ws.onmessage = function(data, state) {        // 收到消息的时候重置倒计时        heartCheck.reset();                //something            }    ws.onerror = function() {        message('error', 'WebSocket连接错误!正在重连');        reconnect();    }    ws.onclose = function () {        console.log('onclose');        reconnect();    }}/** * @method reconnect ws重新连接 * @description lockReconnect防止重复连接,时间戳避免在失败时候会频繁建立ws连接 */function reconnect() {    if(lockReconnect) return;    lockReconnect = true;    //没连接上会一直重连,设置延迟避免请求过多    setTimeout(function () {        createWebSocket();        lockReconnect = false;    }, 2000);}
Vue3 中封装 WebSocket,并加入心跳检测和断开连机制是一种常见的需求,可以保证连接的稳定性和数据通信的可靠性。以下是详细的介绍及步骤: --- ### **一、WebSocket 封装概述** 我们通过 Vue3 的 `composition API` 或全局工具的方式对 WebSocket 进行封装,同时添加以下功能: 1. **心跳检测**:定时发送消息给服务器,判断客户端和服务端是否正常在线。 2. **断开连**:当发生网络异常导致 WebSocket 断开时,自动尝试新建立连接。 --- ### **二、实现代码** #### 1. 创建 WebSocket 工具类 我们可以创建一个独立的 JavaScript 文件(如 `wsService.js`),专门用于管理 WebSocket 的生命周期及相关操作。 ```javascript // wsService.js class WsService { constructor(url) { this.url = url; // WebSocket URL this.socket = null; // WebSocket 实例 this.reconnectCount = 0; // 连次数计数器 this.isReconnecting = false; // 是否正在连标志位 this.pingIntervalId = null; // 心跳检测间隔 ID this.reconnectTimeoutId = null; // 连超时时间 ID this.init(); // 初始化 WebSocket 链接 } init() { if (this.socket === null || this.socket.readyState !== WebSocket.OPEN) { this.connect(); } } connect() { try { console.log('尝试连接 WebSocket...'); this.socket = new WebSocket(this.url); this.socket.onopen = () => { console.log('WebSocket 连接成功'); this.startPingPong(); // 启动心跳检测 this.resetReconnectCounter(); // 成功连接后连计数器 }; this.socket.onerror = error => { console.error('WebSocket 发生错误:', error); }; this.socket.onclose = event => { console.warn(`WebSocket 关闭, code=${event.code}, reason=${event.reason}`); this.restartConnection(); // 自动连 }; this.socket.onmessage = messageEvent => { const data = JSON.parse(messageEvent.data); console.log('收到服务端消息:', data); if (data.type === 'pong') { // 如果是 pong 响应,则处理心跳包返回结果 console.log('心跳响应已收到.'); } else { // 其他业务逻辑... } }; } catch (e) { console.error('WebSocket初始化失败', e.message); } } startPingPong(intervalTime = 5000) { clearInterval(this.pingIntervalId); // 清除之前的定时任务避免复启动 this.pingIntervalId = setInterval(() => { if (this.socket && this.socket.readyState === WebSocket.OPEN) { console.log("向服务器发送心跳..."); this.send({ type: "ping" }); // 客户端主动发 ping 包 } }, intervalTime); } restartConnection(maxRetries = 5, delayMs = 3000) { if (!this.isReconnecting) { this.isReconnecting = true; let retryAttemptsLeft = maxRetries; const reconnectFunc = () => { if (retryAttemptsLeft <= 0) { console.error("超过最大试次数"); return; } setTimeout(() => { console.log(`正在进行第 ${maxRetries - retryAttemptsLeft + 1} 次...`); this.connect(); retryAttemptsLeft--; }, delayMs); }; clearTimeout(this.reconnectTimeoutId); // 确保不会多次触发连函数 this.reconnectTimeoutId = setTimeout(reconnectFunc, delayMs * ++this.reconnectCount); } } resetReconnectCounter() { this.reconnectCount = 0; clearTimeout(this.reconnectTimeoutId); this.isReconnecting = false; } send(data) { if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify(data)); } else { console.error('无法发送数据,当前未处于 OPEN 状态!'); } } close(code = 1000, reason = '') { if (this.socket) { this.socket.close(code, reason); } clearInterval(this.pingIntervalId); clearTimeout(this.reconnectTimeoutId); this.resetReconnectCounter(); } } export default function createWsInstance(wsUrl) { return new WsService(wsUrl); } ``` --- #### 2. 在组件中使用 WebSocket 将上述工具引入到需要使用的 Vue 组件内即可直接实例化并调用相关方法: ```vue <template> <div>WebSocket 示例</div> </template> <script setup> import { onMounted, onUnmounted } from 'vue'; import createWsInstance from './utils/wsService'; const wsUrl = 'wss://example.com/socket'; // 替换实际地址 let wsClient = null; onMounted(() => { wsClient = createWsInstance(wsUrl); wsClient.send({ action: 'subscribe', channel: 'chat' }); window.addEventListener('beforeunload', handleUnload); }); function handleUnload() { if (wsClient) { wsClient.close(1000, '用户离开页面'); } } onUnmounted(() => { if (wsClient) { wsClient.close(); } }); </script> ``` --- ### **三、总结** 以上是对 Vue3 下基于 Composition API 和普通 ES6 类完成的一个简单可靠的 WebSocket 封装示例,其中包含关键的心跳维护以及断线后的智能连设计思路,可以根据实际情况调整参数值等细节内容进一步优化性能!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值