如何实现具备自动重连与心跳检测的WebSocket客户端

本文介绍如何通过原生WebSocket API封装一个具备自动重连、心跳检测、错误恢复等能力的稳健客户端。适用于需要长连接的实时通讯场景(如聊天室、实时数据监控等)。

核心功能亮点

  1. 自动重连机制 - 指数退避策略重连
  2. 心跳保活 - 双向检测连接活性
  3. 消息可靠性 - 失败消息自动重发
  4. 异常处理 - 错误分类处理机制
  5. 状态管理 - 精准控制连接生命周期   
  6. 关键优化点说明

  7. 事件监听优化

    • 改用addEventListener替代onopen等属性赋值,允许多监听器共存
    • 新增错误边界处理,防止初始化失败导致程序崩溃
  8. 智能重连策略

    • 避免网络恢复后仍要等待过长的延迟时间
  9. 状态管理改进

    • 通过属性访问器清晰管理连接状态
  10. 消息可靠性增强

    • 独立队列存储未发送消息
    • 连接恢复时自动重发
  11. 配置与状态分离

    • 独立config对象管理配置参数
    • state对象清晰反映运行时状态
  12.  
    /*** WebSocket客户端实现* @param {string} url - 连接地址* @param {string|string[]} [protocols] - 可选协议*/
    class WebSocketClient {// 默认配置(可通过构造函数覆盖)static config = {HEARTBEAT_INTERVAL: 30000, // 心跳间隔30sHEARTBEAT_TIMEOUT: 10000, // 心跳超时10sMAX_RECONNECT_ATTEMPTS: 5, // 最大重连次数BASE_RECONNECT_DELAY: 1000, // 基础重连延迟MAX_RECONNECT_DELAY: 30000, // 最大重连延迟30sMAX_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);
    };

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

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

    相关文章

    【SpringBoot】——在做一些项目中所学到的新的技术栈和一些小技巧(主要为MQ,详细请看目录和文章)

    &#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大三学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

    0经验cursor开发一款跨端app

    设备&#xff1a;mac电脑cursor 1.输入诉求 我要实现一个跨端的地址应用&#xff0c;使其可以在ios、安卓、小程序和网页端都可以使用。这是一个demo的项目&#xff0c;功能不必要太过复杂&#xff0c;下面需要你和我多次沟通完成这个任务。你先根据我的内容输入&#xff0c…

    Element Ui - 编辑时表单校验信息未清空问题处理

    Element Ui 关闭对话框清空验证消息&#xff0c;清除form表单的操作 首先在对话框 取消按钮 添加 click事件&#xff0c;例如&#xff1a;&#xff08;ps&#xff1a;callOf 里面的addGroupData和ref - - &#xff09; <div slot"footer" class"dialog-foo…

    OpenCV图像加权函数:addWeighted

    1 addWeighted函数 在OpenCV 里&#xff0c;addWeighted 函数的作用是对两个图像进行加权求和&#xff0c;常用于图像融合、图像过渡等场景。函数如下&#xff1a; cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])2 参数解释 src1&#xff1a;第一个输入图…

    Science Robotics 利用机器学习进行鳐鱼的仿生设计

    对于海洋生物而言&#xff0c;生物力学和流体动力学力都会对游泳速度施加物理限制&#xff0c;促使游泳策略和鳍形状的趋同进化。鉴于这些限制是与尺度相关的&#xff0c;如雷诺数&#xff08;Re&#xff09;&#xff0c;这就产生了自然运动缩放定律&#xff0c;该定律根据生物…

    基于ssm的一家运动鞋店的产品推广网站的设计

    项目简介 一家运动鞋店实现了以下功能&#xff1a; 实现了用户在线选择试题并完成答题&#xff0c;在线查看考核分数。管理员管理收货地址管理、购物车管理、字典管理、留言版管理、新闻信息管理、产品管理、产品收藏管理、产品评价管理、产品订单管理、单页数据管理、用户管…

    什么是后训练?大语言模型训练后优化方法综述,87页pdf

    大语言模型&#xff08;LLMs&#xff09;的出现彻底改变了自然语言处理领域&#xff0c;使其在从对话系统到科学探索的各个领域中变得不可或缺。然而&#xff0c;其预训练架构在特定场景中往往表现出局限性&#xff0c;包括推理能力受限、伦理不确定性以及领域特定性能欠佳等问…

    python开发订单查询功能(flask+orm bee)

    1. 搭建python环境。 可以参考其它文档。 此处python使用 3.12 IDE随意&#xff0c;PyCharm 或 Eclipse PyDev也可以。 2. Flask 2.1 安装Flask pip install Flask 2.2 一个最简单的flask实例 创建一个工程&#xff0c; 新建一个 main.py文件&#xff0c; 输入以下内容…

    工作记录 2017-01-11

    工作记录 2017-01-11 序号 工作 相关人员 1 协助BPO进行Billing的工作。 修改邮件上的问题。 更新RD服务器。 郝 更新的问题 1、修改了Patient Insurance的文件上传。 1.1 文件存储改为MedI“EHRWfs”Account“patientInfo”MRN 1.2 “Upload Files” to “Upload/Vie…

    基于javaweb的SpringBoot个人健康管理系统小程序微信小程序设计与实现(源码+文档+部署讲解)

    技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

    b站视频下载工具软件怎么下载

    自行配置FFMPEG环境 请优先选择批量下载&#xff0c;会自处理视频和音频文件。 如果要下载更高质量请登陆。 没有配置FFMPEG下载后会有报错提示&#xff0c;视频音频文件无法合并生成mp4文件 更新批量下载标题&#xff0c;只取视频原标题&#xff0c;B站反爬机制登陆后下载多了…

    简单的模拟法

    1. 鸡兔同笼问题&#xff0c;鸡有2只脚 &#xff0c;兔有4只脚&#xff0c;已知脚数求最多有几只动物 #include <stdio.h>void feet(int x){if(x%2 0){if(x%4 0) printf("max%d,min%d",x/2,x/4);else printf("max%d,min%d",x/2,(x-2)/41);}else …

    【python爬虫】酷狗音乐爬取练习

    注意&#xff1a;本次爬取的音乐仅有1分钟试听&#xff0c;仅作学习爬虫的原理&#xff0c;完整音乐需要自行下载客户端。 一、 初步分析 登陆酷狗音乐后随机选取一首歌&#xff0c;在请求里发现一段mp3文件&#xff0c;复制网址&#xff0c;确实是我们需要的url。 复制音频的…

    概率论的基本知识

    逆概率还不懂&#xff0c;改天再想想。 联合概率 联合概率&#xff08;Joint Probability&#xff09; 是概率论中的一个重要概念&#xff0c;用于描述多个随机变量同时取某些值的概率。联合概率可以帮助我们理解多个变量之间的关系。

    Ceph(1):分布式存储技术简介

    1 分布式存储技术简介 1.1 分布式存储系统的特性 &#xff08;1&#xff09;可扩展 分布式存储系统可以扩展到几百台甚至几千台的集群规模&#xff0c;而且随着集群规模的增长&#xff0c;系统整体性能表现为线性增长。分布式存储的水平扩展有以下几个特性&#xff1a; 节点…

    Pytest自动化测试框架pytest-xdist分布式测试插件

    平常我们功能测试用例非常多时&#xff0c;比如有1千条用例&#xff0c;假设每个用例执行需要1分钟&#xff0c;如果单个测试人员执行需要1000分钟才能跑完&#xff1b; 当项目非常紧急时&#xff0c;会需要协调多个测试资源来把任务分成两部分&#xff0c;于是执行时间缩短一…

    在openEuler-22.03-LTS上利用Ansible轻松部署MySQL 5.7

    一、需求 使用ansible自动化部署mysql二进制部署mysql部署mysql并创建JDBC用户 二、环境信息 本文涉及的代码&#xff0c;配置文件地址&#xff1a; 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1g6y 软件名称版本备注Ansible2.9.27All modules — Ansible Doc…

    使用GitHub Actions实现Git推送自动部署到服务器

    将网站一键部署到服务器的方案很多&#xff0c;比如纯Shell脚本结合SSH、Jenkins等工具。本文将介绍如何利用GitHub Actions这一免费且轻量的CI/CD工具&#xff0c;实现代码推送后自动部署到云服务器。 之前一直在使用github的工作流&#xff0c;确实是一个比较好用的工具。 我…

    网络安全 与 加密算法

    计算机中的网络安全 在本篇中介绍了以下几个方面: 机密性 密码学 对称加密算法(DES, 3DES, AES) 公开秘钥算法 RSA大素数的获取 完整性 散列函数(MD5, SHA-1, 并没有提及算法实现) 报文鉴别(MAC) 数字签名 端点鉴别 应用 SSL(TCP网络安全) 运行时安全 防火墙的基本知…

    DeepSeek-prompt指令-当DeepSeek答非所问,应该如何准确的表达我们的诉求?

    当DeepSeek答非所问&#xff0c;应该如何准确的表达我们的诉求&#xff1f;不同使用场景如何向DeepSeek发问&#xff1f;是否有指令公式&#xff1f; 目录 1、 扮演专家型指令2、 知识蒸馏型指令3、 颗粒度调节型指令4、 时间轴推演型指令5、 极端测试型6、 逆向思维型指令7、…