前端实现websocket类封装

随着Web应用程序的发展,越来越多的人开始利用Websocket技术来构建实时应用程序。Websocket是一种在客户端和服务器之间建立持久连接的协议。这种协议可以在一个单独的连接上实现双向通信。与HTTP请求-响应模型不同,Websocket允许服务器自主地向客户端发送数据。这种实时连接的能力使得Websocket在许多应用场景中得到了广泛的应用。

Websocket技术的优点之一是减少了网络延迟。在传统的HTTP请求-响应模型中,客户端必须不断地向服务器发送请求以获取更新的数据。这种不断的请求-响应循环会占用大量的带宽和处理能力。而Websocket的持久连接可以在服务器有新数据时立即向客户端发送,从而减少了网络延迟和服务器负载。

另一个优点是Websocket可以处理大量的并发连接。在传统的HTTP请求-响应模型中,每个请求都必须在服务器上进行处理,这可能会对服务器造成负载压力。但是,Websocket的持久连接可以在服务器上保持打开状态,从而减少了与每个连接相关的开销。这使得服务器可以处理大量的并发连接而不会降低性能。

Websocket还可以用于实时通信。例如,聊天应用程序可以使用Websocket来实现实时消息传递。在这种情况下,Websocket的持久连接可以在服务器上保持打开状态,以便客户端可以接收实时消息。这种实时通信的能力使得Websocket在许多应用程序中得到了广泛的应用。

总之,Websocket技术在现代Web应用程序中发挥着越来越重要的作用。它可以减少网络延迟和服务器负载,处理大量的并发连接,并提供实时通信能力。因此,如果您正在构建一个需要实时更新的Web应用程序,那么Websocket技术可能是您的理想选择。

封装类实现

import { WebSocketConfigOption } from './WebSocketConfigOption';

export class ReconnectableWebSocket {

   private ws!: WebSocket; // ws实例
   private opt: WebSocketConfigOption; // ws配置项
   private lockReconnect: boolean = false; // 避免ws重复连接
   private isClosingWindow: boolean = false;
   private reconnectTimeout: any;
   private heartSendInterval: any;

   constructor(option: WebSocketConfigOption) {
      if (null === option.url || '' === option.url) {
         throw ('url不能为空');
      }
      this.opt = option;
      this.initWebSocket();
   }

   private initWebSocket() {
      if (null == this.opt.secWebSocketProtocol) {
         this.ws = new WebSocket(this.opt.url);
      } else if (this.opt.secWebSocketProtocol.length == 0) {
         this.ws = new WebSocket(this.opt.url);
      } else {
         this.ws = new WebSocket(this.opt.url, this.opt.secWebSocketProtocol);
      }
      this.initEventHandle();
      window.onbeforeunload = () => {
         this.isClosingWindow = true;
         this.ws.close(); // 当窗口关闭时,主动去关闭websocket连接。
      }
   }

   private initEventHandle() {
      this.ws.onclose = () => {
         console.log('ws连接关闭!' + this.opt.url);
         this.opt.onclose && this.opt.onclose();
         this.heartCheckStop();
         if (!this.isClosingWindow) {
            this.reconnect();
         }
      }
      this.ws.onerror = () => {
         console.log('ws连接错误!' + this.opt.url);
         this.opt.onerror && this.opt.onerror();
         this.heartCheckStop();
         if (!this.isClosingWindow) {
            this.reconnect();
         }
      }
      this.ws.onopen = () => {
         console.log('ws连接成功!' + this.opt.url);
         this.opt.onopen && this.opt.onopen();
         this.heartCheckStart();
      }
      this.ws.onmessage = (event: any) => {
         this.opt.onmessage && this.opt.onmessage(event);
      }
   }

   /** 重连 */
   private reconnect() {
      if (this.lockReconnect) {
         return;
      }
      this.lockReconnect = true;
      this.reconnectTimeout = setTimeout(() => {
         this.initWebSocket();
         this.lockReconnect = false;
      }, 2000);
   }

   /** 关闭重连 */
   private reconnectStop(): void {
      clearTimeout(this.reconnectTimeout);
   }

   /** 开启心跳包保持连接 */
   private heartCheckStart(): void {
      this.ws.send('heartCheck');
      this.heartSendInterval = setInterval(() => {
         this.ws.send('heartCheck');
      }, 5 * 60 * 1000);
   }

   /** 关闭心跳包 */
   private heartCheckStop(): void {
      clearInterval(this.heartSendInterval);
   }

   /** 主动关闭连接 */
   public close(): void {
      this.reconnectStop();
      this.heartCheckStop();
      this.isClosingWindow = true;
      this.ws.close();
   }

}

配置类实现

export type WebSocketConfigOption = {

   url: string;
   secWebSocketProtocol?: Array<string>;
   onopen?: () => void;
   onmessage?: (msg: any) => void;
   onerror?: () => void;
   onclose?: () => void;

}

应用示例

import { WebSocketConfigOption } from '../websocket/WebSocketConfigOption';
import { ReconnectableWebSocket } from '../websocket/ReconnectableWebSocket';
import { InnerMqService } from '../../rx/inner-mq.service';

export class MapMessageConnection {

   private ws!: ReconnectableWebSocket;

   constructor(
      private path: string,
      private innerMqService: InnerMqService,
   ) {
      this.connection();
   }

   /** 连接 */
   private connection(): void {
      let wsConfig: WebSocketConfigOption = {
         url: this.path,
         onopen: () => {
         },
         onerror: () => {
         },
         onmessage: (msg: any) => {
            if (msg.data && msg.data !== '') {
               let data = JSON.parse(msg.data);
               this.innerMqService.pub(data.title, data.content);
            }
         }
      }
      this.ws = new ReconnectableWebSocket(wsConfig);
   }

   /** 断开连接 */
   public disConnection(): void {
      this.ws.close();
   }

}
import { InnerMqClient } from '../../rx/inner-mq.service';
import { SubmitService } from '../../service/submit.service';
import { MapBase } from '../../map/map-base';
import { CommonUtil } from '../../util/common-util';
import { MapPage } from '../../view/page/map/map.page';
import { MapDraw } from '../../map/draw/map-draw';
import { MapWrap } from '../../map/draw/map-wrap';
import { GeoUtil } from "../../map/geo-util";
import { Point } from "../../map/entity/Point";

export class MapMessageProcessor {

   constructor(
      private mqClient: InnerMqClient,
      private submitService: SubmitService,
      private mapBase: MapBase,
      private mapPage: MapPage,
   ) {
      /** 放大 */
      mqClient.sub('ZoomIn').subscribe((res) => {
         mapBase.zoomIn();
      });
      /** 缩小 */
      mqClient.sub('ZoomOut').subscribe((res) => {
         mapBase.zoomOut();
      });
      /** 拖动 */
      mqClient.sub('Pan').subscribe((res) => {
         mapBase.pan();
      });
      /** 显示网格 */
      mqClient.sub('GridSwitch').subscribe((res) => {
         let update;
         if (mapBase.getGridVisible()) {
            mapBase.closeGrid();
            update = false;
         } else {
            mapBase.showGrid();
            update = true;
         }
         let config = mapBase.getMapConfig();
         if (config) {
            config.grid = update;
            CommonUtil.setConfigCache(config);
            mapBase.setMapConfig(config);
         }
      });
      /** 切换图层源 */
      mqClient.sub('SwitchResource').subscribe((res) => {
         // 切换图层
         debugger
         let lastType = mapBase.getCurrentCoordinateType();
         mapBase.switchMapResource(res);
         let currentType = mapBase.getCurrentCoordinateType();
         // 保存设置
         let config = mapBase.getMapConfig();
         if (config) {
            config.layer = res;
            CommonUtil.setConfigCache(config);
            mapBase.setMapConfig(config);
         }
         // 检查坐标类型
         if (lastType != currentType) {
            if (lastType == 'wgs84' && currentType == 'gcj02') {
               mapBase.turnMapFeaturesFromWgs84ToGcj02();
            } else if (lastType == 'gcj02' && currentType == 'wgs84') {
               mapBase.turnMapFeaturesFromGcj02ToWgs84();
            }
         }
         // 回调
         setTimeout(() => {
            mapPage.updateShowInfo();
         });
      });
      /** 绘制类型切换 - */
      mqClient.sub('SwitchDrawType').subscribe((res) => {
         mapBase.setDrawType(res);
      });
      /** 绘制 - */
      mqClient.sub('OpenDraw').subscribe((res) => {
         mapBase.pan();
         mapBase.removeDrawedFeatures();
         mapBase.openDraw({
            drawEnd: () => {
               setTimeout(() => {
                  mapBase.removeDrawInteraction();
               })
            },
            modifyEnd: () => {
            }
         });
      });
      /** 绘制指定多边形并定位 - */
      mqClient.sub('DrawPolygonAndPositioning').subscribe((res) => {
         mapBase.pan();
         mapBase.removeDrawedFeatures();
         let blocks = JSON.parse(res);
         for (let i = 0; i < blocks.length; i++) {
            let points: Array<Point> = [];
            for (let j = 0; j < blocks[i].length; j++) {
               let point = new Point(blocks[i][j].lng, blocks[i][j].lat);
               if (mapBase.getCurrentCoordinateType() == 'wgs84') {
                  points.push(GeoUtil.gcj02_To_wgs84(point));
               } else {
                  points.push(point);
               }
            }
            let feature = MapDraw.createPolygonFeature(points);
            MapWrap.addFeature(mapBase, mapBase.drawLayerName, feature);
         }
         mapBase.setFitviewFromDrawLayer();
      });
      /** fitview - */
      mqClient.sub('Fitview').subscribe((res) => {
         mapBase.setFitviewFromDrawLayer();
      });
      /** 删除绘制 - */
      mqClient.sub('RemoveDrawedShape').subscribe((res) => {
         mapBase.removeDrawedFeatures();
      });
      /** 提交区块下载 - */
      mqClient.sub('SubmitBlockDownload').subscribe((res) => {
         let data = {
            tileName: this.mapBase?.getCurrentXyzName(),
            mapType: CommonUtil.getMapType(this.mapBase?.getCurrentXyzName()),
            tileUrl: this.mapBase?.getCurrentXyzUrlResources(),
            points: this.mapBase?.getDrawedPoints(),
         };
         this.submitService.blockDownload(data).then((r) => {
         });
      });
      /** 提交世界下载 - */
      mqClient.sub('SubmitWorldDownload').subscribe((res) => {
         let data = {
            tileName: this.mapBase?.getCurrentXyzName(),
            mapType: CommonUtil.getMapType(this.mapBase?.getCurrentXyzName()),
            tileUrl: this.mapBase?.getCurrentXyzUrlResources()
         };
         this.submitService.worldDownload(data).then((r) => {
         });
      });
   }

}

如果对您有帮助

感谢支持技术分享,请点赞支持:

技术合作交流qq:2401315930

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

17. 电话号码的字母组合中

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "23" 输出&#…

可运营的Leadshop开源商城小程序源码 +H5公众号+带视频教程

源码简介 Leadshop是一款出色的开源电商系统&#xff0c;具备轻量级、高性能的特点&#xff0c;并提供持续更新和迭代服务。该系统采用前后端分离架构&#xff08;uniappyii2.0&#xff09;&#xff0c;以实现最佳用户体验为目标。 前端部分采用了uni-app、ES6、Vue、Vuex、V…

探究element-ui 2.15.8中<el-input>的keydown事件无效问题

一、问题描述 今天看到一个问题&#xff0c;在用Vue2element-ui 2.15.8开发时&#xff0c;使用input组件绑定keydown事件没有任何效果。 <template><div id"app"><el-input v-model"content" placeholder"请输入" keydown&quo…

docker学习笔记01-安装docker

1.Docker的概述 用Go语言实现的开源应用项目&#xff08;container&#xff09;&#xff1b;克服操作系统的笨重&#xff1b;快速部署&#xff1b;只隔离应用程序的运行时环境但容器之间可以共享同一个操作系统&#xff1b;Docker通过隔离机制&#xff0c;每个容器间是互相隔离…

抬头举手阅读YOLOV8NANO

首先用YOLOV8NANO得到PT模型&#xff0c;转换成ONNX,OPENCV调用&#xff0c;PYTHON,C,ANDROID都可以举手写字阅读YOLOV8NANO

pip 国内镜像源

pip 国内镜像源 部分可用的pip国内镜像源有下面这些&#xff1a; 阿里云 http://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣 http://pypi.douban.com/simple Python官方 https://pypi.python.org/simple/ v2ex http://pypi…

uniapp项目如何引用安卓原生aar插件(避坑指南三)

官方文档说明&#xff1a;uni小程序SDK 【彩带- 避坑知识点】 如果引用原生aar插件&#xff0c;都配置好之后&#xff0c;云打包&#xff0c;报不包含此插件&#xff0c;除了检查以下步骤流程外&#xff0c;还要检查一下是否上打包的原生插件aar流程有问题。 1.第一步在uniapp项…

2023年总结以及2024年的计划

2023年总结以及2024年的计划 文章目录 2023年总结以及2024年的计划复盘工作学习爱情旅游北京之旅苏州之游 房子装修投资理财新的一年展望(2024) ​ 今天是2023年12月24日, 星期日, 今年的第358天, 这一年97.81%的时间已流逝. 好像每年的话题都差不多, 2023年 很快就要结束了, 我…

java球队信息管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web球队信息管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5…

CUDA动态并行

一、简介 1. 综述 动态并行是 CUDA 编程模型的扩展&#xff0c;使 CUDA 内核能够直接在 GPU 上创建新工作并与其同步。 在程序中任何需要的地方动态创建并行性都提供了令人兴奋的功能。 直接从 GPU 创建工作的能力可以减少在主机和设备之间传输执行控制和数据的需要&#xf…

BDD - Python Behave Retry 机制

BDD - Python Behave Retry 机制 引言Behave RetryBehave Retry 应用feature 文件创建 step 文件Retry运行 Behave 并生成 rerun 文件重新运行失败的场景 引言 在日常运行测试用例&#xff0c;有时因为环境不稳定造成一些测试用例跑失败了&#xff0c;如果能将这些失败的测试用…

三年半累亏23亿,圆心科技“带伤”闯关IPO

互联网医疗下半场&#xff0c;圆心科技五闯IPO&#xff01; 12月18日&#xff0c;互联网医疗行业的“独角兽”北京圆心科技集团股份有限公司&#xff08;下称“圆心科技”&#xff09;再次递交招股书&#xff0c;继续向港交所发起上市冲击。 这是其自2021年10月以来第五次递表…

Eclipse安装Jrebel eclipse免重启加载项目

每次修改JAVA文件都需要重新启动项目&#xff0c;加载时间太长&#xff0c;eclipse安装jrebel控件,避免重启项目节省时间。 1、Help->Eclipse Marketplace 2、搜索jrebel 3、Help->jrebel->Configuration 配置jrebel 4、激活jrebel 5、在红色框中填入 http://jrebel…

数据结构学习 Leetcode474 一和零

关键词&#xff1a;动态规划 01背包 一个套路&#xff1a; 01背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要逆序遍历完全背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要正序遍历 目录 题目&#xff1a; 思路&#xff1a; 复杂…

CamSim相机模拟器:极大加速图像处理开发与验证过程

随着图像处理技术的不断发展&#xff0c;相机模拟在图像处理开发和验证中扮演着越来越重要的角色。相机模拟能够模拟真实相机的成像过程&#xff0c;提供高质量的图像输入&#xff0c;使开发人员能够更好地评估和调整图像处理算法。本文将探讨如何通过相机模拟来加速图像处理的…

Vue axios Post请求 403 解决之道

前言&#xff1a; 刚开始请求的时候报 CORS 错误&#xff0c;通过前端项目配置后算是解决了&#xff0c;然后&#xff0c;又开始了新的报错 403 ERR_BAD_REQUEST。但是 GET 请求是正常的。 后端的 Controller 接口代码如下&#xff1a; PostMapping(value "/login2&qu…

【免费分享】1985-2023年全国逐月植被净初级生产力(NPP)数据

1985-2023年全国逐月植被净初级生产力&#xff08;NPP&#xff09;数据 净初级生产力(NPP)是指植物在单位时间单位面积上由光合作用产生的有机物质总量中扣除自养呼吸后的剩余部分&#xff0c;是生产者能用于生长、发育和繁殖的能量值&#xff0c;反映了植物固定和转化光合产物…

2023/12/3 今日得先看的重磅AI新闻

&#x1f4f1; 传 iPhone 设计主管加盟苹果前首席设计师公司&#xff0c;与 OpenAI 合作开发 AI 设备 &#x1f697; 雷军宣布&#xff1a;小米澎湃 OS 启动新标识&#xff0c;「人车家全生态」正式闭环 &#x1f527; OpenAI 竞争对手 Anthropic 预计明年年化营收将达到 8.5…

白盒测试 接口测试 自动化测试

一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&#xff0c;对程序的逻辑结构进行检查&#xff0c;从中获取测试数据。白盒测试的对象基本是源程序&#xff0c;所以它又称为结构测试或逻辑驱动测试&#xff0c;白盒测试方法一般分为…

可视化云监控/安防监控系统EasyCVR视频管理平台播流失败的原因(端口篇)

安防视频监控EasyCVR平台兼容性强&#xff0c;可支持的接入协议众多&#xff0c;包括国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议与SDK&#xff0c;如&#xff1a;海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。平台能将接入的视频…