当前位置: 首页 > news >正文

基于Pinia的WebSocket管理与优化实践(实现心跳重连机制,异步发送)

WebSocket作为一种全双工通信协议,允许服务器和客户端之间建立持久的连接,提供了比传统HTTP请求更为高效的数据交换方式。本文将探讨如何使用Pinia状态管理库在Vue应用中优雅地管理和优化WebSocket连接,以实现稳定、高效的实时数据传输。

环境与依赖

  • 环境:Vue.js项目
  • 依赖:Pinia (pnpm install pinia) 和 vant (pnpm install vant)

项目结构与初始化

在项目中,我们创建了一个名为useWebsocketStore的Pinia store,专门用于管理WebSocket相关的状态和行为。该store包含了一系列关键的状态变量和动作,旨在简化WebSocket的连接、消息处理、心跳机制以及重连策略。

WebSocket Store设计

核心状态

  • socket: WebSocket实例。
  • ConnectSuccess: 连接成功标志。
  • messageHandlers: 存储注册的消息处理器数组。
  • deviceIP: 设备的IP地址。
  • reconnectNum: 重连次数。
  • heartbeatMessage: 心跳消息内容。
  • strWs: 发送消息的字符串形式。
  • sendNum: 发送消息计数。
  • messageNum: 接收消息计数。
  • message: 最近接收的消息内容。
    动作详解
  • connectWs: 初始化WebSocket连接,包括建立连接、监听事件、启动心跳,并提供Promise接口确保异步操作的完成。
  • registerMessageHandler & unregisterMessageHandler:允许外部组件注册或注销消息处理器,实现了插拔式的事件处理机制。
  • handleMessageFromWebSocket: 处理从WebSocket接收到的消息,更新内部状态并调用所有注册的消息处理器。
  • handleClose: WebSocket关闭时的处理逻辑,包括重连尝试、状态重置及错误通知。
  • **`sendMessage: 发送消息至WebSocket,包含基本的发送逻辑。
  • sendMessageWithRetry: 异步发送消息,自动处理连接重试,增强了消息发送的健壮性。
  • **sendHeartbeat: 发送心跳消息,维护连接活性。
  • **startHeartbeat&stopHeartbeat: 控制心跳定时器的启动与停止,确保资源的有效利用。

为什么需要心跳机制

心跳机制(Heartbeat Mechanism)主要用于保持网络连接的活跃状态,尤其是在TCP/IP连接中,如HTTP长轮询、WebSocket、TCP长连接等场景中。它通过周期性地发送小量数据(通常称为心跳包)来确认连接的双方仍然存活,进而避免因网络层的超时或中间代理(如负载均衡器、防火墙)的会话超时而导致的连接意外断开。

useWebsocketStore.js

import { defineStore } from 'pinia'import { showToast } from 'vant'export const useWebsocketStore = defineStore('websocket', {state: () => ({socket: null,ConnectSuccess: false,// 新增一个数组用于存储注册的处理器messageHandlers: [],deviceIP: null,reconnectNum: 0,heartbeatMessage: JSON.stringify({ cmd: '1111' }), // 心跳消息内容strWs: null,sendNum: 0,messageNum: 0,message: 0}),getters: {},actions: {async connectWs(deviceIP) {if (!this.socket && !this.ConnectSuccess) {console.log('connectWs', deviceIP)this.deviceIP = deviceIPthis.socket = new WebSocket(deviceIP)// this.socket.onopen = this.handleOpenthis.socket.onmessage = this.handleMessageFromWebSocketthis.socket.onclose = this.handleClose// this.socket.onerror = this.handleErrorreturn new Promise((resolve, reject) => {this.socket.onopen = (event) => {console.log('WebSocket 连接已建立', event)this.ConnectSuccess = truethis.startHeartbeat()resolve()}this.socket.onerror = (event) => {console.error('WebSocket 连接错误', event)this.ConnectSuccess = falsereject(event)}})}},// 新增方法用于注册消息处理器registerMessageHandler(handler) {this.messageHandlers.push(handler)},// 新增方法用于移除消息处理器unregisterMessageHandler(handler) {this.messageHandlers = this.messageHandlers.filter((h) => h !== handler)},//handleMessageFromWebSocket(event) {const message = JSON.parse(event.data)console.log('消息', message)this.message = message.cmdthis.messageNum += 1// 调用所有注册的处理器// console.log(message)this.messageHandlers.forEach((handler) => handler(message))},handleOpen(event) {console.log('WebSocket 连接已建立', event)if (event.target.readyState === 1) {this.ConnectSuccess = true}},handleClose(event) {console.error('WebSocket 关闭:', event)this.socket = nullthis.ConnectSuccess = falsethis.sendNum = 0this.messageNum = 0this.stopHeartbeat()// 添加自动重连逻辑this.reconnectWs().then(() => {this.ConnectSuccess = trueshowToast('WebSocket 重连成功')}).catch((error) => {this.ConnectSuccess = falseshowToast('最终重连失败', error)})},handleError(error) {console.error('WebSocket 错误:', error)this.ConnectSuccess = false},async reconnectWs() {return new Promise(async (resolve, reject) => {this.reconnectNum++try {console.log(`尝试重新连接... (${this.reconnectNum})`)await this.connectWs(this.deviceIP)if (this.socket && this.ConnectSuccess) {resolve() // 连接成功,停止重试} else {throw new Error('连接失败')}} catch (error) {this.ConnectSuccess = falseconsole.log(`重连WebSocket失败,稍后重试... (${this.reconnectNum})`)reject()}})},sendMessage(message) {// 发送消息逻辑...if (this.socket.readyState === WebSocket.OPEN) {// showToast('确定发送', message)this.strWs = JSON.parse(message).cmdthis.sendNum += 1this.socket.send(message)} else {// 如果在此之后仍然无法发送,可以选择抛出错误或继续等待(根据需求)showToast('WebSocket 尚未连接')console.error('即使重试后,WebSocket仍无法发送消息')}},// 在actions中添加一个新的异步发送方法async sendMessageWithRetry(message) {if (!this.ConnectSuccess) {// 如果连接未建立,尝试重新连接try {await this.reconnectWs()} catch (error) {console.error('尝试重新连接WebSocket失败', error)throw error}}this.sendMessage(message)},// 发送心跳消息的方法sendHeartbeat() {this.sendMessageWithRetry(this.heartbeatMessage)},// 启动心跳定时器startHeartbeat() {this.heartbeatInterval = setInterval(() => {this.sendHeartbeat()}, 5000) // 每隔3秒发送一次心跳},// 如果需要在适当的时候清理心跳定时器,比如在断开连接时stopHeartbeat() {if (this.heartbeatInterval) {clearInterval(this.heartbeatInterval)this.heartbeatInterval = null}}}
})

.vue 文件中使用

useWebsocket.connectWs(deviceIP) 连接socket 只需要连接一次,可以放在main.js 里面初始化的时候连接

<script setup>
import {  onMounted, onUnmounted,  } from 'vue'import { useWebsocketStore } from '@/stores/useWebsocketStore'
const useWebsocket = useWebsocketStore()const deviceIP='ws:192.168.89'
const handleMessage= (message)=>{// 外部处理消息逻辑
}
const init = () => {useWebsocket.connectWs(deviceIP) // useWebsocket.registerMessageHandler(handleMessage)
}
init()onUnmounted(() => {// 组件卸载时移除消息处理器useWebsocket.unregisterMessageHandler(handleMessage)})

相关文章:

基于Pinia的WebSocket管理与优化实践(实现心跳重连机制,异步发送)

WebSocket作为一种全双工通信协议&#xff0c;允许服务器和客户端之间建立持久的连接&#xff0c;提供了比传统HTTP请求更为高效的数据交换方式。本文将探讨如何使用Pinia状态管理库在Vue应用中优雅地管理和优化WebSocket连接&#xff0c;以实现稳定、高效的实时数据传输。 环境…...

Perl词法作用域:自定义编程环境的构建术

&#x1f3ad; Perl词法作用域&#xff1a;自定义编程环境的构建术 在Perl编程中&#xff0c;词法作用域&#xff08;lexical scoping&#xff09;是一种控制变量可见性的方式&#xff0c;它允许变量在特定的作用域内可见&#xff0c;从而避免变量名的冲突。Perl提供了灵活的机…...

vscode使用ssh连接远程服务器

开工啦 vscode连接远程服务器&#xff08;傻瓜式教学&#xff09; 正常根据上面文章的步骤就可以连接了 报错可以尝试的文章&#xff1a; VScode通过remote ssh连接虚拟机 & 报错过程试图写入的管道不存在&#xff08;已解决&#xff09; vscode remote ssh linux[血泪…...

linux 常用和不那么常用命令记录02 磁盘占用

常用的磁盘相关命令 du 有的时候我们想要查询一个文件所占用的磁盘空间大小&#xff0c;可以使用du命令来查看 命令 配置 参数 du [options] [files or directories]-h&#xff1a;以人类可读的格式显示输出&#xff08;例如 KB、MB、GB&#xff09;。 -s&#xf…...

mybatis日志记录方案

首先对指定表进行监控 对表进行监控,那么就要使用的是statementInterceptor 拦截器 使用拦截器那么就要写intercepts写拦截条件进行拦截 监控只对与增删改 查询不进行监控 对于字段的监控,是谁修改了字段,那么就进行报警,或者提醒 消息提醒使用钉钉机器人进行消息提醒 P…...

【LeetCode】最长连续序列

目录 一、题目二、解法完整代码 一、题目 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nu…...

Windows下终端Kafka指令常用操作

1、创建Topic kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test 2、查看Topic列表 kafka-topics.bat --list --bootstrap-server localhost:9092 3、设置Topic最大消息大小 kafka-topics.bat --bootstrap-s…...

QT---lineEdit相关信号

1.returnPressed信号 connect(ui.lineEdit_passWord, &QLineEdit::returnPressed, []() { // 输入密码回车后&#xff0c;调用校验密码接口ui.lineEdit_passWord->clearFocus(); //失去焦点on_param_confirmBtn_clicked();});2.输入后失去焦点才获取编辑框内新信息 参…...

基于vue的地图特效(飞线和标注)

这段代码的主要功能是在页面加载完成后&#xff0c;初始化一个 echarts 地图图表&#xff0c;并配置了相关的地理数据、散点数据、线条数据以及样式效果&#xff0c;最后在指定的 div 元素中进行展示。 需要再vue中的框架实现&#xff0c;不能单独直接运行。 标注 type: effe…...

生物环保技术有哪些缺点或者局限性呢

生物环保技术&#xff0c;作为一种利用生物学原理和技术来处理环境污染的方法&#xff0c;虽然具有绿色环保、高效节能等优点&#xff0c;但也存在一些缺点和局限性。以下是对这些缺点和局限性的详细分析&#xff1a; 一、受环境因素影响大 生物环保技术的效果往往受到环境因…...

我被手机所伤,竟如此憔悴。

临睡前&#xff0c;刚刷完小视频&#xff0c;感觉好无聊。一阵阵空虚感袭来。看看时间&#xff0c;哦&#xff0c;原来我下班后一直从6点刷视频到11点。 哎&#xff0c;太空虚了&#xff0c;又马上要睡觉了&#xff0c;为什么会这么难受呢?明明我大学&#xff0c;高中&#x…...

【深度学习】第3章实验——回归模型

根据相关数据集进行回归分析 1. import statsmodels.api as sm # df.loc[:, ...] 表示选择所有行。 # df.columns ! mpg 创建一个布尔数组&#xff0c;指示哪些列不等于 mpg。 # df.loc[:, df.columns ! mpg] 选择 df 中所有行和列名不等于 mpg 的所有列。 x df.loc[:,df.col…...

MYSQL 四、mysql进阶 8(索引优化与查询优化)

都有哪些维度可以进行数据库调优&#xff1f;简言之&#xff1a; 索引失效、没有充分利用到索引——建立索引关联查询太多JOIN&#xff08;设计缺陷或不得已的需求&#xff09;——SQL优化服务器调优及各个参数设置&#xff08;缓冲、线程数等&#xff09;——调整my.cnf数据过…...

python | pyvips,一个神奇的 Python 库

本文来源公众号“python”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;pyvips&#xff0c;一个神奇的 Python 库&#xff01; 大家好&#xff0c;今天为大家分享一个神奇的 Python 库 - pyvips。 Github地址&#xff1a;https…...

STM32利用FreeRTOS实现4个led灯同时以不同的频率闪烁

在没有接触到FreeRTOS时&#xff0c;也没有想过同时叫两个或两个以上的led灯闪烁的想法&#xff0c;接触后&#xff0c;发现如果想叫两个灯同时以不同的频率闪烁&#xff0c;不能说是不可能&#xff0c;就算是做到了也要非常的麻烦。但是学习了FreeRTOS后&#xff0c;发现要想同…...

深入Laravel事件系统:创建与使用事件的指南

Laravel的事件系统是一种强大的机制&#xff0c;它允许你将应用程序的行为封装成事件&#xff0c;然后在适当的时候触发这些事件。这不仅有助于代码的解耦&#xff0c;还提高了应用程序的可维护性和可扩展性。本文将详细介绍如何在Laravel中创建和使用事件&#xff0c;包括事件…...

element-ui操作表格行内容如何获取当前行索引?

需求&#xff1a; 根据每个用户的提交次数、撤回次数&#xff0c;动态计算出实际次数&#xff0c;并且提交次数不能小于撤回次数 <template><div><el-table:data"tableData"style"width: 80%"border><el-table-columnprop"date&…...

代发考生战报:南京考场华为售前HCSP H19-411考试通过

代发考生战报&#xff1a;南京考场华为售前HCSP H19-411考试通过&#xff0c;客服给的题库非常稳定&#xff0c;考试遇到2个新题&#xff0c;剩下全是题库里的原题&#xff0c;想考的放心考吧&#xff0c;考场服务挺好&#xff0c;管理员带着做签名和一些考试说明介绍清楚&…...

【Spring Boot】Spring原理:Bean的作用域和生命周期

目录 Spring原理一. 知识回顾1.1 回顾Spring IOC1.2 回顾Spring DI1.3 回顾如何获取对象 二. Bean的作用域三. Bean的生命周期 Spring原理 一. 知识回顾 在之前IOC/DI的学习中我们也用到了Bean对象&#xff0c;现在先来回顾一下IOC/DI的知识吧&#xff01; 首先Spring IOC&am…...

MinIO:开源对象存储解决方案的领先者

MinIO:开源对象存储解决方案的领先者 MinIO 是一款开源的对象存储系统&#xff0c;致力于提供高性能、可伸缩、安全的数据存储解决方案。 官方解释&#xff1a;MinIO 是一个基于Apache License v2。0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...