HOW - 保证 WebSocket 持续正常连接

一、基于 React 的 WebSocket

下面是一个React版本的WebSocket连接代码示例,展示了如何在React组件中实现WebSocket连接、心跳机制以及自动重连功能。

WebSocketManager.js

首先,我们可以创建一个 WebSocketManager 类来封装WebSocket的逻辑,包括连接、心跳和重连。

// WebSocketManager.jsclass WebSocketManager {constructor(url) {this.url = url;this.ws = null;// Ping和Pong是websocket里的心跳,用来保证客户端是在线的,一般来说只有服务端给客户端发送Ping,然后客户端发送Pong来回应,表明自己仍然在线this.pingInterval = null;this.pongTimeout = null;this.reconnectDelay = 5000; // 5秒后重新连接}connect() {this.ws = new WebSocket(this.url);this.ws.onopen = () => {console.log('Connected');this.startHeartbeat();};this.ws.onclose = (event) => {console.log(`Connection closed: ${event.reason}`);clearInterval(this.pingInterval);clearTimeout(this.pongTimeout);setTimeout(() => this.connect(), this.reconnectDelay);};this.ws.onerror = (error) => {console.error(`WebSocket error: ${error.message}`);};this.ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.type === 'pong') {console.log('Received pong');clearTimeout(this.pongTimeout);} else {this.handleMessage(message);}};}startHeartbeat() {this.pingInterval = setInterval(() => {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify({ type: 'ping' }));this.pongTimeout = setTimeout(() => {console.warn('Pong timeout, closing connection');this.ws.close();}, 5000);}}, 30000);}handleMessage(message) {// 处理接收到的其他消息console.log('Received message:', message);}send(data) {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify(data));}}
}export default WebSocketManager;

App.js

然后,在React组件中使用这个 WebSocketManager

// App.jsimport React, { useEffect } from 'react';
import WebSocketManager from './WebSocketManager';const App = () => {useEffect(() => {const wsManager = new WebSocketManager('ws://example.com/socket');wsManager.connect();// 清理函数,组件卸载时关闭 WebSocket 连接return () => {if (wsManager.ws) {clearInterval(wsManager.pingInterval);clearTimeout(wsManager.pongTimeout);wsManager.ws.close();}};}, []);return (<div className="App"><h1>WebSocket Demo</h1>{/* 其他组件或内容 */}</div>);
};export default App;

解释

  1. WebSocketManager 类

    • 封装 WebSocket 连接的逻辑,包含连接、心跳和重连功能。
    • connect 方法用于建立 WebSocket 连接并设置相应的事件处理器。
    • startHeartbeat 方法实现心跳机制,每隔30秒发送一次 ping 消息,如果5秒内没有收到 pong 响应,则关闭连接并触发重连。
    • handleMessage 方法处理接收到的其他消息,可以根据需要进行扩展。
    • send 方法用于发送消息。
  2. App 组件

    • 使用 useEffect 在组件挂载时创建 WebSocketManager 实例并建立连接,在组件卸载时清理定时器和关闭连接。

通过这种方式,可以在 React 项目中实现稳定的 WebSocket 连接,并具备自动重连和心跳检测功能。

注意:避免无限制重连

上面的示例中,客户端会一直尝试重连。每当连接关闭时,都会在5秒后重新尝试连接。这样可以确保在网络暂时中断或服务器暂时不可用的情况下,客户端能够自动恢复连接。

如果希望更精细地控制重连行为,例如限制重连次数或逐步增加重连间隔,可以进行以下改进:

  1. 限制重连次数

    • 可以增加一个变量来记录重连次数,并在超过最大重连次数后停止重连。
  2. 逐步增加重连间隔

    • 可以使用指数退避(exponential backoff)算法来逐步增加重连间隔,以避免频繁重连。

下面是改进后的 WebSocketManager 类:

// WebSocketManager.jsclass WebSocketManager {constructor(url) {this.url = url;this.ws = null;this.pingInterval = null;this.pongTimeout = null;this.reconnectDelay = 5000; // 初始重连延迟this.maxReconnectDelay = 30000; // 最大重连延迟this.reconnectAttempts = 0; // 重连次数this.maxReconnectAttempts = 10; // 最大重连次数}connect() {this.ws = new WebSocket(this.url);this.ws.onopen = () => {console.log('Connected');this.reconnectAttempts = 0; // 重置重连次数this.reconnectDelay = 5000; // 重置重连延迟this.startHeartbeat();};this.ws.onclose = (event) => {console.log(`Connection closed: ${event.reason}`);clearInterval(this.pingInterval);clearTimeout(this.pongTimeout);if (this.reconnectAttempts < this.maxReconnectAttempts) {this.reconnectAttempts++;setTimeout(() => this.connect(), this.reconnectDelay);this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay); // 指数退避} else {console.log('Max reconnect attempts reached. Giving up.');}};this.ws.onerror = (error) => {console.error(`WebSocket error: ${error.message}`);};this.ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.type === 'pong') {console.log('Received pong');clearTimeout(this.pongTimeout);} else {this.handleMessage(message);}};}startHeartbeat() {this.pingInterval = setInterval(() => {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify({ type: 'ping' }));this.pongTimeout = setTimeout(() => {console.warn('Pong timeout, closing connection');this.ws.close();}, 5000);}}, 30000);}handleMessage(message) {// 处理接收到的其他消息console.log('Received message:', message);}send(data) {if (this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify(data));}}
}export default WebSocketManager;

解释:

  1. 限制重连次数

    • 增加了 reconnectAttemptsmaxReconnectAttempts 变量,用于记录和限制重连次数。
    • onclose 事件中,检查 reconnectAttempts 是否小于 maxReconnectAttempts,如果是则继续重连,否则停止重连。
  2. 逐步增加重连间隔

    • 使用 reconnectDelaymaxReconnectDelay 变量控制重连延迟。
    • 在每次重连后,使用 Math.min(this.reconnectDelay * 2, this.maxReconnectDelay) 增加重连延迟,直到达到最大重连延迟。

通过这种改进,可以更好地控制重连行为,避免客户端无限制地尝试重连,并在重连失败后逐步增加重连间隔。

当然,这个时间间隔应该最好可以比「服务端重启 Websocket」来的久,这样就可以在服务端出现故障重启时正常重连。

二、为什么会 ping 不通

当客户端一直无法 ping 通服务器时,可能会发生以下几种情况:

  1. 连接断开

    • 如果客户端无法接收到服务器的 pong 响应,可能意味着连接已经断开或存在网络问题。在这种情况下,客户端需要采取措施进行重连。
  2. 服务器无响应

    • 如果服务器因故未能回复 pong 消息,客户端会认为连接已失效。同样需要尝试重连。
  3. 连接超时

    • 为了防止无限等待,客户端可以设置一个超时时间。如果在指定时间内没有收到 pong 响应,就认为连接失效并进行重连。

三、保证持续正常连接的最佳实践

保证 WebSocket 持续正常连接可以通过以下几个步骤和最佳实践:

2.1 定时发送心跳消息 (Heartbeat/Ping-Pong)

定时发送心跳消息来保持连接活跃。服务器和客户端可以定期发送 Ping 和 Pong 消息,以确保连接未被中断。

const ws = new WebSocket('ws://example.com/socket');const interval = setInterval(() => {if (ws.readyState === WebSocket.OPEN) {ws.send(JSON.stringify({ type: 'ping' }));}
}, 30000); // 每30秒发送一次心跳消息ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.type === 'pong') {// 处理心跳回复}
};

2.2 自动重连机制

当连接断开时,自动尝试重新连接。

let ws;function connect() {ws = new WebSocket('ws://example.com/socket');ws.onopen = () => {console.log('Connected');};ws.onclose = (event) => {console.log(`Connection closed: ${event.reason}`);setTimeout(connect, 5000); // 5秒后重新连接};ws.onerror = (error) => {console.error(`WebSocket error: ${error.message}`);};ws.onmessage = (event) => {// 处理接收到的消息};
}connect();

2.3 检测网络状态

通过监听网络状态变化来处理网络断开和恢复。

window.addEventListener('online', () => {console.log('Network is online. Attempting to reconnect...');connect();
});window.addEventListener('offline', () => {console.log('Network is offline. Closing WebSocket connection...');ws.close();
});

2.4 处理服务器端断开

服务器端可以通过发送特定消息通知客户端即将断开,客户端收到后可进行相应处理。

ws.onmessage = (event) => {const message = JSON.parse(event.data);if (message.type === 'server-close') {console.log('Server is closing the connection');ws.close();}
};

2.5 增加异常处理

在连接的生命周期内,添加对各种异常情况的处理,以确保程序不会因为未处理的异常而崩溃。

ws.onerror = (error) => {console.error(`WebSocket error: ${error.message}`);// 可以根据错误类型进行不同的处理,例如重连、提示用户等
};

通过上述方法,可以提高 WebSocket 连接的稳定性和持续性,确保在网络状况良好时,连接可以保持正常。如果遇到网络波动或其他异常情况,也能及时进行恢复和重连。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46921.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web前端 Vue 框架面试120题(六)

面试题 101 . 如何解决Vuex页面刷新数据丢失 &#xff1f; 参考回答&#xff1a; F5页面刷新&#xff0c;页面销毁之前的资源&#xff0c;重新请求&#xff0c;因此写在生命周期里的vuex数据是重新初始化&#xff0c;无法获取的&#xff0c;这也就是为什么会打印出空的原因。…

解决Oracle SQL语句性能问题——正确使用Hint(Hint概念、场景及具体语法)

10.5. 正确使用Hint 10.5.1. Hint概念及场景 调优SQL语句时,Oracle提供了很多可用的Hint。首先,你应该获取和分析SQL语句的执行计划,看能否通过改写SQL语句或其他方法来进行调整和优化,而不是直接使用Hint方法进行优化,最好在其他方法都确定无效或不合理之后再考虑使用H…

HTTPS 的加密过程 详解

HTTP 由于是明文传输&#xff0c;所以安全上存在以下三个风险&#xff1a; 窃听风险&#xff0c;比如通信链路上可以获取通信内容。篡改风险&#xff0c;比如通信内容被篡改。冒充风险&#xff0c;比如冒充网站。 HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议&#xff0c…

概率论原理精解【4】

文章目录 度量空间概述理论基础定义特点高级概念广泛应用 性质例子应用 柯西数列柯西数列的定义柯西数列的例子 参考文献 度量空间 概述 设 f : R n → R m , f ˙ ( x ) 在 { x : ∣ x − x 0 ∣ < r } 内连续&#xff0c;则当 ∣ t ∣ < r 时&#xff0c; f:R^n\righ…

Spring Cloud LoadBalanced

负载均衡(Load Balance&#xff0c;简称 LB) 是⾼并发, ⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时, 通常会采⽤增加机器的⽅式进⾏扩容, 负载均衡就是⽤来在多个机器或者其他资源中, 按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 就像是eureka中对请求进行轮询的…

Java对象创建过程的解析

Java对象创建过程的解析 1. 类的加载与连接2. 内存分配2.1 分配方式2.2 本地线程缓冲分配&#xff08;TLAB&#xff09; 3. 初始化内存4. 设置对象头 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 对象的创建是一个涉及多个步骤的复杂过程…

Qt:26.Qt项目:贪吃蛇游戏

一、项目功能演示&#xff1a; 开始界面可以点击进入游戏。 点击进入游戏之后&#xff0c;切换到选项界面&#xff0c;该界面可以选择游戏难度&#xff0c;回退&#xff0c;以及查询最近一次游戏得分。 游戏具体界面如下。贴图啥的可以自己换&#xff0c;本人审美不咋行&#x…

[SUCTF 2019]EasySQL1

这是一个简单的SQL注入题&#xff0c;但是因为我的SQL基础约等于0&#xff0c;所以做起来很难。 首先试试引号是否被过滤 可以看到单引号、双引号都被过滤了&#xff0c;试试其他的盲注都不行&#xff0c;基本上可以确定不能用这种方法。 在测试的过程中发现&#xff0c;输入…

RICHTEK立锜科技 WIFI 7电源参考设计

什么是WIFI 7? WiFi 7&#xff08;Wi-Fi 7&#xff09;是下一代Wi-Fi标准&#xff0c;对应的是IEEE 802.11将发布新的修订标准IEEE 802.11be –极高吞吐量EHT&#xff08;Extremely High Throughput &#xff09;。Wi-Fi 7是在Wi-Fi 6的基础上引入了320MHz带宽、4096-QAM、Mu…

oceanbase架构、功能模块、数据存储、特性、sql流转层等概念详解

一、架构图 OceanBase 数据库采用无共享&#xff08;Shared-Nothing&#xff09;分布式集群架构&#xff0c;各个节点之间完全对等&#xff0c;每个节点都有自己的 SQL 引擎、存储引擎、事务引擎&#xff0c;运行在普通 PC 服务器组成的集群之上&#xff0c;具备高可扩展性、高…

cephrgw元数据和数据布局

提示&#xff1a;每个rados object有如下几个组成部分&#xff0c;分别是omap&#xff08;omapheader、omapkey、omapval&#xff09;、xattr、data&#xff0c;相关的CLI command rados getomapheader {radosobjectname} -p {poolname} [--namespace{ns}] rados listomapkeys…

Eureka基本概念

Eureka基本概念 Eureka基本概念 一、服务消费者如何获取服务提供者的信息&#xff1f; 不管是消费服务者还是消费提供者&#xff0c;都是服务&#xff0c;服务启动之后会注册到eureka的注册中心去&#xff0c;当服务消费者请求调用某个服务的时候&#xff0c;会根据eureka注册…

【LabVIEW作业篇 - 4】:属性节点赋值和直接节点赋值的区别体现

文章目录 属性节点赋值和直接节点赋值的区别体现 属性节点赋值和直接节点赋值的区别体现 创建5个圆形指示灯&#xff0c;然后循环点亮&#xff0c;先给圆形指示灯赋值假变量&#xff0c;然后再进行循环。 运行结果&#xff0c;观察结果&#xff0c;发现刚开始运行时&#xff0…

引领小模型潮流!OpenAI发布功能强大且成本低的GPT-4o mini

GPT-4o mini的成本比GPT-3.5 Turbo低了超过60%&#xff0c;其聊天表现优于Google的Gemini Flash和Anthropic的Claude Haiku。该模型从周四开始对ChatGPT的免费用户、ChatGPT Plus用户和团队订阅用户开放&#xff0c;并将在下周向企业用户开放。OpenAI计划未来将图像、视频和音频…

Vue 自定义组件编写 案例实战

index.vue <template><div><el-button type"primary" click"showDialog">添加邮递配置</el-button><el-dialog :title"dialogTitle" :visible.sync"dialogVisible" width"800px" :before-clos…

【Leetcode】一、排序

文章目录 1、选择排序2、冒泡排序3、插入排序 1、选择排序 给定数组arr&#xff0c;其长度为n。实现思路&#xff1a; 遍历数组&#xff0c;从0 ~ n - 1&#xff0c;找到最小的&#xff0c;找到后&#xff0c;和数组的第一个元素互换位置继续新一轮遍历&#xff0c;从1 ~ n -…

路网双线合并单线——ArcGIS 解决方法

路网双线合并成单线是一个在地图制作、交通规划以及GIS分析中常见的需求。双线路网定义&#xff1a;具有不同流向、不同平面结构的道路。此外&#xff0c;车道数较多的道路&#xff08;例如&#xff0c;双黄实线车道数大于4的道路&#xff09;也可以视为双线路网&#xff0c;本…

扩容升级丨极海正式推出G32A1465系列汽车通用MCU,驱动智驾再进阶

继2023年推出G32A系列汽车通用平台首发产品G32A1445系列后&#xff0c;极海宣布正式推出G32A1465系列全新汽车通用MCU&#xff0c;以满足日益增长的智能驾驶应用需求。作为升级迭代产品&#xff0c;G32A1465专为应用范围不断扩大的高运算要求而设计&#xff0c;集成丰富的通信接…

数据结构(5.2_3)——二叉树的存储结构

二叉树的顺序存储 #define MAXLEN 255struct TreeNode {ElemType value;//结点中的数据元素bool isEmpty;//结点是否为空 };void main() {TreeNode t[MaxSize]; } 定义一个长度为MaxSize的数组t&#xff0c;按照从上至下、从左至右的顺序依次存储完全二叉树中的各个结点 几个…

前端组件化探索与实践:Vue自定义暂无数据组件的开发与应用

摘要 随着前端开发技术的不断进步&#xff0c;组件化开发已成为提升开发效率、降低维护成本的关键手段。本文旨在通过介绍一款Vue自定义暂无数据组件的开发与实践&#xff0c;深入探讨前端组件化开发的重要性、优势及其在实际项目中的应用。 一、引言 在前端开发中&#xff0…