基于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作为一种全双工通信协议,允许服务器和客户端之间建立持久的连接,提供了比传统HTTP请求更为高效的数据交换方式。本文将探讨如何使用Pinia状态管理库在Vue应用中优雅地管理和优化WebSocket连接,以实现稳定、高效的实时数据传输。 环境…...
Perl词法作用域:自定义编程环境的构建术
🎭 Perl词法作用域:自定义编程环境的构建术 在Perl编程中,词法作用域(lexical scoping)是一种控制变量可见性的方式,它允许变量在特定的作用域内可见,从而避免变量名的冲突。Perl提供了灵活的机…...
vscode使用ssh连接远程服务器
开工啦 vscode连接远程服务器(傻瓜式教学) 正常根据上面文章的步骤就可以连接了 报错可以尝试的文章: VScode通过remote ssh连接虚拟机 & 报错过程试图写入的管道不存在(已解决) vscode remote ssh linux[血泪…...
linux 常用和不那么常用命令记录02 磁盘占用
常用的磁盘相关命令 du 有的时候我们想要查询一个文件所占用的磁盘空间大小,可以使用du命令来查看 命令 配置 参数 du [options] [files or directories]-h:以人类可读的格式显示输出(例如 KB、MB、GB)。 -s…...
mybatis日志记录方案
首先对指定表进行监控 对表进行监控,那么就要使用的是statementInterceptor 拦截器 使用拦截器那么就要写intercepts写拦截条件进行拦截 监控只对与增删改 查询不进行监控 对于字段的监控,是谁修改了字段,那么就进行报警,或者提醒 消息提醒使用钉钉机器人进行消息提醒 P…...
【LeetCode】最长连续序列
目录 一、题目二、解法完整代码 一、题目 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: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, []() { // 输入密码回车后,调用校验密码接口ui.lineEdit_passWord->clearFocus(); //失去焦点on_param_confirmBtn_clicked();});2.输入后失去焦点才获取编辑框内新信息 参…...
基于vue的地图特效(飞线和标注)
这段代码的主要功能是在页面加载完成后,初始化一个 echarts 地图图表,并配置了相关的地理数据、散点数据、线条数据以及样式效果,最后在指定的 div 元素中进行展示。 需要再vue中的框架实现,不能单独直接运行。 标注 type: effe…...
生物环保技术有哪些缺点或者局限性呢
生物环保技术,作为一种利用生物学原理和技术来处理环境污染的方法,虽然具有绿色环保、高效节能等优点,但也存在一些缺点和局限性。以下是对这些缺点和局限性的详细分析: 一、受环境因素影响大 生物环保技术的效果往往受到环境因…...
我被手机所伤,竟如此憔悴。
临睡前,刚刷完小视频,感觉好无聊。一阵阵空虚感袭来。看看时间,哦,原来我下班后一直从6点刷视频到11点。 哎,太空虚了,又马上要睡觉了,为什么会这么难受呢?明明我大学,高中&#x…...
【深度学习】第3章实验——回归模型
根据相关数据集进行回归分析 1. import statsmodels.api as sm # df.loc[:, ...] 表示选择所有行。 # df.columns ! mpg 创建一个布尔数组,指示哪些列不等于 mpg。 # df.loc[:, df.columns ! mpg] 选择 df 中所有行和列名不等于 mpg 的所有列。 x df.loc[:,df.col…...
MYSQL 四、mysql进阶 8(索引优化与查询优化)
都有哪些维度可以进行数据库调优?简言之: 索引失效、没有充分利用到索引——建立索引关联查询太多JOIN(设计缺陷或不得已的需求)——SQL优化服务器调优及各个参数设置(缓冲、线程数等)——调整my.cnf数据过…...
python | pyvips,一个神奇的 Python 库
本文来源公众号“python”,仅用于学术分享,侵权删,干货满满。 原文链接:pyvips,一个神奇的 Python 库! 大家好,今天为大家分享一个神奇的 Python 库 - pyvips。 Github地址:https…...
STM32利用FreeRTOS实现4个led灯同时以不同的频率闪烁
在没有接触到FreeRTOS时,也没有想过同时叫两个或两个以上的led灯闪烁的想法,接触后,发现如果想叫两个灯同时以不同的频率闪烁,不能说是不可能,就算是做到了也要非常的麻烦。但是学习了FreeRTOS后,发现要想同…...
深入Laravel事件系统:创建与使用事件的指南
Laravel的事件系统是一种强大的机制,它允许你将应用程序的行为封装成事件,然后在适当的时候触发这些事件。这不仅有助于代码的解耦,还提高了应用程序的可维护性和可扩展性。本文将详细介绍如何在Laravel中创建和使用事件,包括事件…...
element-ui操作表格行内容如何获取当前行索引?
需求: 根据每个用户的提交次数、撤回次数,动态计算出实际次数,并且提交次数不能小于撤回次数 <template><div><el-table:data"tableData"style"width: 80%"border><el-table-columnprop"date&…...
代发考生战报:南京考场华为售前HCSP H19-411考试通过
代发考生战报:南京考场华为售前HCSP H19-411考试通过,客服给的题库非常稳定,考试遇到2个新题,剩下全是题库里的原题,想考的放心考吧,考场服务挺好,管理员带着做签名和一些考试说明介绍清楚&…...
【Spring Boot】Spring原理:Bean的作用域和生命周期
目录 Spring原理一. 知识回顾1.1 回顾Spring IOC1.2 回顾Spring DI1.3 回顾如何获取对象 二. Bean的作用域三. Bean的生命周期 Spring原理 一. 知识回顾 在之前IOC/DI的学习中我们也用到了Bean对象,现在先来回顾一下IOC/DI的知识吧! 首先Spring IOC&am…...
MinIO:开源对象存储解决方案的领先者
MinIO:开源对象存储解决方案的领先者 MinIO 是一款开源的对象存储系统,致力于提供高性能、可伸缩、安全的数据存储解决方案。 官方解释:MinIO 是一个基于Apache License v2。0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
