当前位置: 首页 > 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)})

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C# 用户权限界面的测试内容
  • (数据大屏)(Hadoop)基于SSM框架的学院校友管理系统的设计与实现+文档
  • 1119 胖达与盆盆奶
  • 深入Django(八)
  • 【网络安全】第6讲 黑客与网络攻击(笔记)
  • c++ word转换为pdf
  • 【路径规划】基于A星算法实现机器人栅格地图径规划附Matlab代码
  • 02归并排序——分治递归
  • CV01_相机成像原理与坐标系之间的转换
  • 工作助手VB开发笔记(1)
  • 数据库——数据库性能优化
  • 【Linux】:进程创建与终止
  • MySQL主从复制_腾讯云
  • 2024 年 6 月区块链游戏研报:Pixels 引发 DAU 波动,行业用户留存率差异显著
  • mongodb-docker-compos-安装
  • 「译」Node.js Streams 基础
  • eclipse(luna)创建web工程
  • export和import的用法总结
  • JS字符串转数字方法总结
  • LeetCode29.两数相除 JavaScript
  • mysql中InnoDB引擎中页的概念
  • npx命令介绍
  • orm2 中文文档 3.1 模型属性
  • Python - 闭包Closure
  • Spring声明式事务管理之一:五大属性分析
  • ucore操作系统实验笔记 - 重新理解中断
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • #FPGA(基础知识)
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (1)Jupyter Notebook 下载及安装
  • (C++17) std算法之执行策略 execution
  • (Java)【深基9.例1】选举学生会
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (多级缓存)多级缓存
  • (二)springcloud实战之config配置中心
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (算法设计与分析)第一章算法概述-习题
  • (转)linux 命令大全
  • (转)程序员疫苗:代码注入
  • . NET自动找可写目录
  • .Net 基于.Net8开发的一个Asp.Net Core Webapi小型易用框架
  • .net 受管制代码
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • [ C++ ] STL---string类的模拟实现
  • [Android]创建TabBar
  • [BT]BUUCTF刷题第9天(3.27)
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [C++] 模拟实现list(二)
  • [CareerCup] 14.5 Object Reflection 对象反射