uniapp自定义websocket类实现socket通信、心跳检测、连接检测、重连机制

uniapp自定义websocket类实现socket通信、心跳检测、检测连接、重连机制,仿vue-socket插件功能实现发送序列号进行连接检测,发送消息时42【key,value】格式,根据后端返回数据和需要接收到的数据做nsend与onSocketMessage的修改

import {publish,subscribe,unsubscribe
} from 'pubsub-js'
import {isArray
} from "@/utils/validate"const noop = function() {};
class Socket {//心跳检测定时器static stopTime = 0;//每25s发送报文给服务端监听连接是否正常static sendInterval = 0;//服务端监听连接异常时提示并进行socket重连static isCommunication = false;//连接中出错调用心跳检测方法的定时器static errorNr = '';//websocket已连接数static concatCount = 0;//连接后连接设备次数(用于拼接传输序号42后的数字)static sendTag = 0//pc端emit发送消息有回调函数时加入static isCallback = ["leave", "join"]//连接或断连publish相应isCallback数组内的值给客户端响应static sendKey = "";//new Socket时执行构造方法,对参数默认值进行初始化constructor({url = '',onOpen = noop,onMsg = noop,onClose = noop,onError = noop,onReload = noop,onRdFinsh = noop,maxInterValCount = 10,interValTime = 2000,...args} = {}) {this.isRconnectIng = false; //是否处于重连状态this.waiting = Promise.resolve(false); //心跳检查必须等待重连完成后this.waitDep = []; //等待时收集依赖的容器this.SocketTask = {nsend: noop,hbDetection: noop,nclose: noop,nrconnect: noop,isconnect: false,uniColse: false,maxInterValCount,interValTime,InterValCount: 0,eventPatch: null,url,onOpen,onMsg,onClose,onError,onReload,onRdFinsh,extra: args};//为对象提供连接方法this._EventDispath(this.SocketTask);//初始化websocketthis.initChat(this.SocketTask, this.SocketTask.extra);return this.SocketTask;}set CONCATCOUNT(value) {Socket.concatCount = value;if (value > 0) this._notify();}get CONCATCOUNT() {return Socket.concatCount}/*** 每25s给服务端发送一次数据,确保连接未断开*/initSendTime() {Socket.sendInterval = setInterval(() => {if (Socket.isCommunication) {uni.showToast({icon: 'none',title: '网络异常',duration: 2000})this.SocketTask.nrconnect()} else {Socket.isCommunication = truethis.SocketTask.nsend(2)}}, 25000)subscribe("number", function(msg, data) {Socket.isCommunication = false})}//销毁定时器destroySend() {clearTimeout(Socket.errorNr)clearTimeout(Socket.stopTime);clearInterval(Socket.sendInterval)unsubscribe("number")}/*** 仅供内部使用,通知所有收集到的依赖*/_notify() {for (let i = 0; i < this.waitDep.length; i++) {this.waitDep[i].call(this.SocketTask);}this.waitDep = [];}/*** 仅供内部使用,确认当前是否连接成功,收集依赖*/_chunkConnect(fn) {//没有连接时收集起来,当连接成功时开始发送命令等操作if (Socket.concatCount > 0) {fn();} else {this.waitDep.push(fn);}}/*** 仅供内部使用,事件注册*/_EventDispath({onReload} = {}) {let SocketTask = this.SocketTask;let events = {onOpen: [],onMsg: [],onClose: [],onError: [],onReload: [],onRdFinsh: [],}SocketTask.hbDetection = () => {this.hbDetection()}//适配后端websocket发送请求数据//vue-socket-io 传入的是数组,第一位为key值SocketTask.nsend = (key, value) => {let that = thisthis._chunkConnect(() => {let data = "42"//web端连接或断开连接的时候传入420 421if (Socket.isCallback.includes(key)) {//收集当前连接的命令key值,等待返回响应时传递给客户端处理逻辑Socket.sendKey = key//421 422 ··· 依次递增data += Socket.sendTagSocket.sendTag++}if (value) {data += JSON.stringify([key, value])} else {//特殊情况,当想要发送的请求不为42【key,value】格式时,key为客户端代码发送传入的值data = key + ""}uni.sendSocketMessage({//pc端emit事件有发送完毕回调函数时后端会需要有多一位数字的判断,并且返回时也会产生eg:420 -》 430// 没有回调函数时发送和接收命令前面都是42data: data,complete(e) {},})})}SocketTask.nclose = t => {this.destroySend()this._chunkConnect(() => {SocketTask.uniColse = true;uni.closeSocket();})}SocketTask.nrconnect = t => {this._chunkConnect(() => {this.waiting = new Promise(async (resolve) => {uni.closeSocket();let reloadStatus = false;try {const res = await this.initChat(SocketTask, SocketTask.extra);reloadStatus = res;} catch (e) {}onReload.call(SocketTask, reloadStatus, SocketTask);SocketTask.eventPatch.dispatchEvent('onReload', reloadStatus);resolve(reloadStatus);})})}function EventDispatcher() {this.events = events;}for (let key in events) {//绑定监听方法到EventDispatcher方法原型中EventDispatcher.prototype[key] = function(handler) {if (typeof handler != 'function') return;this.events[key].push(handler)}}//调用监听方法 监听连接状态EventDispatcher.prototype.dispatchEvent = function(type, msg) {let evenArr = this.events[type];if (evenArr.length > 0) {for (let i = 0; i < evenArr.length; i++) {evenArr[i].call(SocketTask, msg, SocketTask);}}}SocketTask.eventPatch = new EventDispatcher();}/*** 心跳检测*/async hbDetection() {const SocketTask = this.SocketTask;if (SocketTask.uniColse) {return false;}clearTimeout(Socket.stopTime);if (!SocketTask.isconnect) { //未连接则启动连接if (SocketTask.maxInterValCount > SocketTask.InterValCount) {Socket.stopTime = setTimeout(async () => {try {const R_result = await this.waiting;if (R_result) return;this.isRconnectIng = true;const openResult = await this.initChat(SocketTask, SocketTask.extra);if (openResult) {SocketTask.InterValCount++;return;}return this.hbDetection();} catch (e) {return this.hbDetection();}}, SocketTask.interValTime)} else {SocketTask.onRdFinsh.call(SocketTask, SocketTask.maxInterValCount, SocketTask);SocketTask.eventPatch.dispatchEvent('onRdFinsh', SocketTask.maxInterValCount);}}}/*** websocket监听事件*/SocketEvents({onOpen,onMsg,onClose,onError,onReload,} = {}) {return new Promise((resolve, reject) => {const SocketTask = this.SocketTask;uni.onSocketOpen(res => {this.CONCATCOUNT += 1;this.isRconnectIng = false;SocketTask.isconnect = true;Socket.sendTag = 0Socket.sendKey = ""SocketTask.InterValCount = 0;SocketTask.uniColse = false;resolve(true);this.initSendTime()onOpen.call(SocketTask, res, SocketTask);SocketTask.eventPatch.dispatchEvent('onOpen', res)})//适配服务器端返回的消息格式uni.onSocketMessage(msg => {let serialNumber = 0let arrayIndex = msg.data.indexOf("[")let objIndex = msg.data.indexOf("{")//判断返回值是纯数字或json格式let dataStart = arrayIndex != -1 && objIndex != -1 ?(arrayIndex < objIndex ? arrayIndex : objIndex) :(arrayIndex == -1 ? objIndex : arrayIndex)if (dataStart == -1) {serialNumber = msg.data.substring(0)publish("number", serialNumber)} else {serialNumber = msg.data.substring(0, dataStart)let jsonData = JSON.parse(msg.data.substring(dataStart))//判断是否为连接事件if (serialNumber == `43${Socket.sendTag - 1}`) {publish(Socket.sendKey, jsonData[0])} else if (isArray(jsonData)) {publish(jsonData[0], jsonData[1])}}onMsg.call(SocketTask, msg, SocketTask);SocketTask.eventPatch.dispatchEvent('onMsg', msg)})uni.onSocketClose(async err => {this.destroySend()SocketTask.isconnect = false;SocketTask.InterValCount = 0;//微信小程序 连接失败会自动关闭连接,返回false给心跳监测方法resolve(false);if (!this.isRconnectIng) {this.hbDetection();}onClose.call(SocketTask, err, SocketTask);SocketTask.eventPatch.dispatchEvent('onClose', err);})uni.onSocketError(err => {this.destroySend()Socket.errorNr = setTimeout(() => {this.hbDetection()}, 3000)onError.call(SocketTask, err, SocketTask);SocketTask.eventPatch.dispatchEvent('onError', err)})})}/*** 开始初始化chat*/initChat({url,onOpen,onMsg,onClose,onError,onReload} = {}, args) {return new Promise(async (resolve, reject) => {try {await this.connectSocket(url, args);let res = await this.SocketEvents({onOpen,onMsg,onClose,onError,onReload,})resolve(res);} catch (e) {console.log('initChat', e)reject();}})}/*** 连接webSocket*/connectSocket(url, args) {return new Promise((resolve, reject) => {uni.connectSocket({url,success: () => {resolve();},fail: err => {reject();},...args})})}
}
export default Socket

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

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

相关文章

leetcode-没有重复项的全排列-97

题目要求 思路 1.递归&#xff0c;如果num和n的元素个数一样就可以插入res中了&#xff0c;这个作为递归的结束条件 2.因为这个题是属于排列&#xff0c;并非组合&#xff0c;两者的区别是排列需要把之前插入的元素在回退会去&#xff0c;而组合不需要&#xff0c;因此会存在一…

14【PS作图】像素画尺寸大小

【背景介绍】本节介绍像素图多大合适 下图是160*144像素大小,有一个显示文本的显示器,还有一个有十几个键的键盘 像素画布尺寸 电脑16像素,但还有一个显示屏 下图为240*160 在场景素材,和对话素材中,用的是不同尺寸的头像,对话素材中的头像会更清楚,尺寸会更大 远处…

【软考高项】三十三、质量管理

一、管理基础 质量定义 国际标准&#xff1a;反映实体满足主体明确和隐含需求的能力的特性总和。 国家标准&#xff1a;一组固有特性满足要求的程度。固有特性是指在某事或某物中本来就有的&#xff0c;尤其是那种永久的可区分的特征。 ➢ 对产品来说&#xff0c;例如…

Flask 路由基础和封装

Flask 路由 Flask中的路由是用来定义应用程序中的 URL 和处理函数之间的映射关系的&#xff0c;而URL则是用户访问应用程序的入口点。通过路由,我们可以将用户访问的 URL 映射到对应的视图函数上,从而实现不同的功能。 一、路由基础 1.定义路由: 我们可以使用 app.route() …

查看微信小程序主包大小

前言 略 查看微信小程序主包大小 在微信开发者工具右上角找到“详情->基本信息” 查看微信小程序主包构成 通过微信开发者工具中的“代码依赖分析”工具查看

置身事内 书摘

信息的重要性&#xff1a;所谓山高皇帝远&#xff0c;上级领导不可能掌握和处理所有信息&#xff0c;故常常不得不依赖下级提供的信息&#xff0c;内容是否可靠&#xff0c;上级不见得知道&#xff0c;因此可能被下级牵着鼻子走。但因为信息复杂&#xff0c;不易处理&#xff0…

Unity 性能优化之光照优化(七)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、测试目的一、实时光源是什么&#xff1f;二、开始测试1.场景中只有一个光照的数值情况2.添加4个点光源后4.结果 总结 前言 实时光源数量越多&#x…

分享一个国内可用的AIGC网站,免费无限制,支持AI绘画

背景 AIGC作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 作为一个AI爱好者&#xff0c;翻遍了各大基于AIGC的网站&#xff0c;终于找到一个免费&#xff01;免登陆&#xff01;手机电脑通用&#xff01;国内可直接对话的AIGC&am…

保持亮灯:监控工具如何确保 DevOps 中的高可用性

在快速发展的 DevOps 领域&#xff0c;保持高可用性 (HA) 至关重要。消费者期望应用程序具有全天候响应能力和可访问性。销售损失、客户愤怒和声誉受损都是停机的后果。为了使 DevOps 团队能够在问题升级为中断之前主动检测、排除故障并解决问题&#xff0c;监控工具成为这种情…

nginx--tcp负载均衡

mysql负载均衡 安装mysql yum install -y mariadb-server systemctl start mariadb systemctl enable mariadb ss -ntl创建数据库并授权 MariaDB [(none)]> create database wordpress; Query OK, 1 row affected (0.00 sec)MariaDB [(none)]> grant all privileges o…

设计模式(十一):外观模式

设计模式&#xff08;十一&#xff09;&#xff1a;外观模式 1. 外观模式的介绍2. 外观模式的类图3. 外观模式的实现3.1 创建一个接口3.2 创建接口的实现3.3 创建一个外观类3.4 测试 1. 外观模式的介绍 外观模式&#xff08;Facade Pattern&#xff09;属于结构型模式&#xf…

数据结构与算法之经典排序算法

一、简单排序 在我们的程序中&#xff0c;排序是非常常见的一种需求&#xff0c;提供一些数据元素&#xff0c;把这些数据元素按照一定的规则进行排序。比如查询一些订单按照订单的日期进行排序&#xff0c;再比如查询一些商品&#xff0c;按照商品的价格进行排序等等。所以&a…

lua的类型,lua_State,函数调用

类型 lua中的数据可以这样分为两位&#xff1a;值类型和引用类型。引用类型创建时需要从堆上分配内存&#xff0c;复制时只需要复制指针&#xff0c;分配的内存由GC负责维护生命期。 所有lua类型都用一个union来表示&#xff1a; /* ** Union of all Lua values */ typedef …

IT养生知识之:子午流注

《子午流注口诀》 肺寅大卯胃辰宫&#xff0c; 脾巳心午小未中&#xff0c; 申膀酉肾心包戌&#xff0c; 亥焦子胆丑肝通。 何为子午流注&#xff1f; 子午流注是中医圣贤发现的一种规律&#xff0c;中医认为人体中十二条经脉对应着每日的十二个时辰&#xff0c;由于时辰在…

【Osek网络管理测试】[TG3_TC5]等待总线睡眠状态_1

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT在满足进入等待睡眠状态的条件时是否进入该状态 …

C#算法之堆排序算法

算法释义&#xff1a;堆排序算法的基本原理&#xff0c;其实就是利用二叉堆的数据结构&#xff0c;通过构建一个最大堆&#xff0c;然后将堆顶元素&#xff08;最大值&#xff09;与末尾元素交换&#xff0c;接着缩小堆的大小并重新调整堆&#xff0c;直到堆中只剩下一个元素。…

WP Rocket插件下载:加速您的WordPress网站,提升用户体验

在互联网速度决定用户体验的今天&#xff0c;一个快速加载的网站对于吸引和保留访问者至关重要。WP Rocket插件&#xff0c;作为一款专为WordPress设计的高性能缓存插件&#xff0c;提供了一套完整的解决方案&#xff0c;帮助您优化网站性能&#xff0c;提升用户体验。 [WP Ro…

Django实验(远程访问+图片显示)

众所周知&#xff0c;Python除了不能生孩子什么都会。Python也是可以做web服务的。 Python做web有一个重点优势是&#xff1a;做一个快速的AI Demo。 第一步&#xff1a;安装一个版本5.0以上django 第二步&#xff1a;构建咱们的Django工程&#xff0c;我取名为BBQ django-adm…

flask 前后台文件多张图片api;AIGC streamlit、gradio多图片页面展示

1、flask 前后台文件多张图片api send_file 传递zip: send_file(zip_data, mimetype=‘application/zip’, as_attachment=True, download_name=‘images.zip’) from flask import Flask, Response, request,send_file from PIL import Image import torch import io from …

【copilot 使用指南 - @workspace】

为什么需要workspace 默认情况下&#xff0c;copilot只能分析当前文件中的代码内容&#xff0c; 那么如何让copliot 跨文件分析&#xff0c;分析整个项目&#xff0c;分析整个代码目录下的代码&#xff0c;就要用到workspace&#xff0c;举例 &#xff1a;假设如下代码 index…