websocket具体实践

websocket具体实践

参考:
如何使用websocket
WebSocket客户端连接不上和掉线的问题以及解决方案

    继6月份对websocket一顿了解之后,我们的项目也要上websocket了,虽然这部分不是我做,但是借此机会,我也想要尝试一下:假如是我来写这个模块,我会写成什么样呢?
抱着这样的想法,在11月份开始着手查了一些资料,打算完整地写一份前端的socket实例,一开始完全没有头绪,网上查了查大家写的都很简略的样子,达不到想要的效果,后来写小程序的时候想到可以借鉴小程序里的websocket封装,然后终于确定了类的api和基本功能。
    修改过几次后,实现的功能和流程如下:(当然我自己写的这一份只是在自己的网站里做了一些简单测试,里面肯定还有很多bug待解决的)

  • 1.0 第一版:引入scoketio客户端依赖做的,已经全面否决
  • 1.1 第二版:使用函数实现,实际使用感乱七八糟的。
  • 1.2 第三版:借鉴小程序的类封装,定义好了简单的success/fail/onError API 实现
  • 1.3 第四版:增加多场景(1、时效性强不需要重连机制,连接失败立马更换轮询场景。2、保持长连接的可重连场景),增加心跳、断线重连
  • 1.4 第五版:断线后不会发送close事件,心跳事件仍然照常发送,故取消断线重连的机制,但是检测定时器仍然保留。
    疑问:
    1. 心跳事件如果只有客户端发送保活,后端收到后不响应,会不会有什么问题?
    • 基本不会有问题,保活是为了确保正确的连接到俩端,出于服务器性能考虑可以这样做。
    1. 如果断线后没有检测到连接断开,会不会重连时多个客户端连接到同一个后端上去了,这样有办法检测吗?
    • 后端可以检测连接的websocket session,把之前的取消掉,否则可能会出现服务端发送了消息但是客户端接受不到的情况,
编写中遇到的问题:
  1. 重连需要注意把之前的连接关闭,否则就会出现内存泄漏的问题,如下图一下开了好多个连接,后端也收到好多个连接

  2. websocket和MQ消息队列有什么不同,为什么不采用MQ.
  • MQ没办法精准定位到用户,如果有多个用户需要消息,往消息队列中取消息不知道哪个才是自己的。
  • 前端获取MQ信息不像后端那么好操作易上手,而ws是html支持的API

前端代码实现如下:
后端是使用express-ws简单地建立了一个本地服务器,代码不贴了吧,网上老多了。

const READY_STATE = {CONNECTING: 'CONNECTING',OPEN: 'OPEN',CLOSING: 'CLOSING',CLOSED: 'CLOSED',0: 'CONNECTING',1: 'OPEN',2: 'CLOSING',3: 'CLOSED',
};/*** eg: new Socket('/connect',{success:(ws)=>{}, fail:(e)=>{switch(e.code){}}, onMessage:(res)=>{} })* onclose 如果允许重连则重连,不允许则直接回调fail* onerror 仅在后台发出警示,回调中不开放这个回调,一切在fail函数中处理* @param url 需要连接的ws链接,仅 /+pathname host拼接* @param success 连接成功后调用* @param fail 连接失败 除了以下code,其他为未知bug*             code=1 -> 不支持websocket*             code=2 -> 不允许重连-连接失败、允许重连-重连三次后也还是连接失败*             code=3 -> 身份验证未通过*             code=4 -> 未收到心跳事件主动断开连接*             code=5 -> (暂时不处理) 因为客户端断线 / 服务端主动断开等原因导致连接关闭后的回调* @param onMessage 收到消息的回调函数。* @param allowReConnect 允许连接失败后再重新连接而不立刻返回失败结果。(比如结果页就不需要重连,只需要第一次连接的结果,否则重连几次后,支付结果都出来了)* 连接成功后将发送身份验证,每次连接发送数据需要带上本次会话的发送时间戳(不然前端无法确定本次回复是哪次消息)* ? 如果意外在同一个页面同时创建了多个链接,前端无法检测出来,因为对象之间互相不联系,需要后端查询是否有相同用户链接,主动断开。*/
class Socket {constructor(connectUrl = '', options = {}) {this.initData(connectUrl, options);this.connect();}initData = (connectUrl, options) => {this.connectUrl = connectUrl;this.props = options;// socket连接事件this.socketOpen = false;this.socketConnecting = false;this.socketMsgQueue = []; // 待发送、发送后未收到回复 的消息队列this.maxSocketConnectCount = 3; // 最多断线重连3次this.socketConnectCount = 0; // 断线重连次数this.socketConnectTimer = null; // 断线重连定时器// 心跳事件this.heartBeatFailCount = 0; // 心跳连接失败次数this.heartBeatTimer = null; // 心跳事件定时器this.heartBeatEventCb = null; // 心跳事件回调函数,一旦回应则清空,未回应则重连或关闭连接。};/*** 建立连接* @param {string} from reconnet | undefined  默认为初始化连接,reconnet为连接失败重连*/connect = (from = 'connect') => {try {if (!window.WebSocket) {this.options.fail({ code: 1, error: '不支持' });}const ws = new WebSocket('ws://localhost:8100' + this.connectUrl);this.socketConnecting = true;// 注册ws相关事件ws.onopen = () => {console.log('success');this.ws = ws;this.socketOpen = true;this.socketConnecting = false;this.cancelReConnect();this.ping();};ws.onerror = (e) => {this.socketOpen = false;this.socketConnecting = false;console.error('ERROR:' + from + '-连接失败');};ws.onclose = () => {console.error('CLOSE:' + from + '-连接失败');this.socketOpen = false;this.cancelHeartBeat();if(this.socketConnecting){ws.close();this.socketConnecting = false;}if (this.props.allowReConnect) {this.reConnect();} else {this.props.fail?.({ code: 2, error: '连接失败' });}};} catch (error) {this.options.fail({ code: 1000, error });}};/** 取消重连 */cancelReConnect = () => {if (this.socketConnectTimer) {clearTimeout(this.socketConnectTimer);this.socketConnectTimer = null;this.socketConnectCount = 0;}};/** 重连 */reConnect = () => {if (this.socketConnectCount < this.maxSocketConnectCount) {this.socketConnectCount = this.socketConnectCount + 1;this.socketConnectTimer = setTimeout(()=>{this.connect();},1000)} else {this.socketConnectCount = 0;clearTimeout(this.socketConnectTimer);this.socketConnectTimer = null;}};onmessage = (e, cb) => {if (e.data === 'pong') {// 清空当前的心跳回调事件clearTimeout(this.heartBeatEventCb);this.heartBeatEventCb = null;}console.log('from server: ' + e.data);cb(e);};// 关闭心跳事件cancelHeartBeat = () => {clearInterval(this.heartBeatTimer);this.heartBeatTimer = null;};// 注册心跳事件ping = () => {if (this.heartBeatTimer) {this.cancelHeartBeat();}this.heartBeatTimer = setInterval(() => {this.ws.send('ping');// 向心跳事件列表push一个心跳事件,30秒发送一次,setTimeOut 10s后没有收到Pong消息则重连或关闭连接。this.heartBeatEventCb = setTimeout(()=>{this.ws.close();this.props.fail({code:4,error:'未收到心跳事件'})},1 * 10 * 1000)}, 1 * 30 * 1000);};
}export default Socket;

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

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

相关文章

『运维备忘录』之 Kubernetes(K8S) 常用命令速查

一、简介 kubernetes&#xff0c;简称K8s&#xff0c;是用8代替名字中间的8个字符“ubernete”而成的缩写&#xff0c;是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用。kubernetes是基于容器技术的分布式架构解决方案&#xff0c;具有完备的集群管理能力&a…

C++初阶篇----新手进村

目录 一、什么是C二、C关键字三、命名空间3.1命名空间的定义3.2命名空间的使用 四、C输入和输出五、缺省参数5.1缺省参数的概念5.2缺省参数的分类 六、函数重载6.1函数重载的概念6.2函数重载的原理----名字修饰 七、引用7.1引用概念7.2引用特性7.3常引用7.4引用的使用7.5传值、…

【Larry】英语学习笔记语法篇——从句=连词+简单句

目录 三、从句连词简单句 1、必须有连词 主从结构 疑问词的词性 2、名词性从句 同位语从句 形式主语 形式宾语 that的省略 3、形容词性从句&#xff08;上&#xff09; 关系代词 关系词的作用 介词前置问题 4、形容词性从句&#xff08;中&#xff09; 定语关系…

飞天使-k8s知识点14-kubernetes散装知识点3-Service与Ingress服务发现控制器

文章目录 Service与Ingress服务发现控制器存储、配置与角色 Service与Ingress服务发现控制器 在 Kubernetes 中&#xff0c;Service 和 Ingress 是两种不同的资源类型&#xff0c;它们都用于处理网络流量&#xff0c;但用途和工作方式有所不同。Service 是 Kubernetes 中的一个…

Electron基本介绍

Electron基本介绍 Electron 官方网站&#xff1a;https://www.electronjs.org/zh/ Electron安装方法&#xff1a;npm install electron -g 全局安装 Electron简介&#xff1a;Electron提供了丰富的本地&#xff08;操作系统&#xff09;API&#xff0c;使你能够使用纯JavaScr…

【Boost】:http_server模块(六)

http_server模块 一.安装cpp-httplib库二.基本使用服务器 一.安装cpp-httplib库 可以自己写一个http服务器&#xff0c;但比较麻烦&#xff0c;这里直接使用库。 在gitee上搜索cpp-httplib&#xff0c;任意找一个即可&#xff08;建议使用0.7.15版本&#xff09;。例如&#xf…

[word] word参考文献怎么对齐 #学习方法#微信#笔记

word参考文献怎么对齐 word参考文献怎么对齐&#xff1f; 未对齐的参考文献如下 全部选中参考文献内容 选中段落快捷窗口显示/隐藏编辑标记快捷方式和标号快捷方式中左对齐 选中之后参考文献又自动加了标号 把之前的角标和文字之间全部删除 完成图

5G技术对物联网的影响

随着数字化转型的加速&#xff0c;5G技术作为通信领域的一次重大革新&#xff0c;正在对物联网&#xff08;IoT&#xff09;产生深远的影响。对于刚入行的朋友们来说&#xff0c;理解5G技术及其对物联网应用的意义&#xff0c;是把握行业发展趋势的关键。 让我们简单了解什么是…

【DDD】学习笔记-领域模型与函数范式

函数范式 REA 的 Ken Scambler 认为函数范式的主要特征为&#xff1a;模块化&#xff08;Modularity&#xff09;、抽象化&#xff08;Abstraction&#xff09;和可组合&#xff08;Composability&#xff09;&#xff0c;这三个特征可以帮助我们编写简单的程序。 通常&#…

单片机学习笔记---DS1302时钟

上一节我们讲了DS1302的工作原理&#xff0c;这一节我们开始代码演示。 新创建一个工程写上框架 我们需要LCD1602进行显示&#xff0c;所以我们要将LCD1602调试工具那一节的LCD1602的模块化代码给添加进来 然后我们开始创建一个DS1302.c和DS1302.h 根据原理图&#xff0c;为了…

学习Android的第八天

目录 Android ImageView 图像视图 ImageView 的基本使用 src属性和background属性的区别 范例 解决 anndroid:blackground 属性拉伸导致图片变形的方法 设置透明度的问题 范例 android:src 和 android:background 结合 范例 Java 代码中设置 blackground 和 src 属性…

Unity 编辑器篇|(五)编辑器拓展GUILayout类 (全面总结 | 建议收藏)

目录 1. 前言2. 参数3. 功能3.1 按钮&#xff1a;Button、RepeatButton3.2 文本&#xff1a;Label、TextArea、TextField、PasswordField3.3 工具栏&#xff1a;Toolbar3.4 切换框&#xff1a;Toggle3.5 滚动条&#xff1a;HorizontalScroll 、VerticalScroll3.6 滑条&#xff…