双向通信之Websocket
介绍
Websocket是一种在单个TCP连接上进行全双工通信的协议。与传统的HTTP协议不同,websocket允许客户端与服务器之间的双向通信,可以在同一条连接上进行多次消息的快速传递。我之前在做一个线上刷题网站的时候,需要设计一个社区讨论模块,当有用户评论了我的动态内容或者回复了我的评论的时候我需要被通知,那么这边我就使用了websocket来实现消息回复的实时通知功能。
Websocket协议
原理
它的工作原理大致分为以下这么几个部分:
1.建立连接:客户端在初次加载的时候通过HTTP向服务端发起建立websocket连接的请求,服务端接收到之后同意连接,那么服务端就会将HTTP连接升级到websocket连接。这里分为两步:
握手请求:客户端向服务端发送的HTTP请求包含关键性头部:Connection:upgrade,upgrade:websocket,代表要求升级到websocket协议;sec-websocket-key是基于base64编码生成的校验值,用于服务端对请求进行校验。
握手响应:服务端在接收到请求之后,校验通过会生成101switching code返回,代表升级协议成功,同时还有sec-websocket-Accept,代表请求校验的合法性。
2.数据传输:建立连接成功之后双方就可以发送数据帧了,数据帧的关键性字段包括有:FIN代表这是最后一帧,Opcode代表数据传输的格式(文本帧/二进制帧等),负载数据负载长度等等。
3.关闭连接:客户端和服务端任何一方都可以关闭连接,也可以是网络中断关闭连接。
4.心跳机制:为了确保双方通信的活跃性,客户端或者服务端可以随时向对方发送ping帧,对方接收到之后返回一个pong帧。
5.安全性:websocket可以运行在TLS协议上确保传输的安全性,比如大家熟知的HTTPS就是HTTP+TLS。
业务代码实现
这里我们将给出我们项目中使用websocket实现的消息回复实时通知的部分代码。
首先我们需要做一些配置,我们要求前端请求中有一个"erp"项用于存放用户的openid,方便我们用于作唯一标识来识别不同的用户。
@Component
public class WebSocketServerConfig extends ServerEndpointConfig.Configurator {@Overridepublic boolean checkOrigin(String originHeaderValue) {return true;}@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {Map<String, List<String>> parameterMap = request.getParameterMap();List<String> erpList = parameterMap.get("erp");if(!CollectionUtils.isEmpty(erpList)){sec.getUserProperties().put("erp", erpList.get(0));}}}
接着就是我们自定义websocket类的编写,在这边我们将实现消息的推送、异常处理、客户端的连接与关闭的逻辑。首先是连接成功与关闭的逻辑:
/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, EndpointConfig conf) throws IOException {//获取用户信息try {Map<String, Object> userProperties = conf.getUserProperties();String erp = (String) userProperties.get("erp");this.erp = erp;this.session = session;if (clients.containsKey(this.erp)) {clients.get(this.erp).session.close();clients.remove(this.erp);onlineCount.decrementAndGet();}clients.put(this.erp, this);onlineCount.incrementAndGet();log.info("有新连接加入:{},当前在线人数为:{}", erp, onlineCount.get());sendMessage("连接成功", this.session);if(offlineMessages.containsKey(this.erp)){ConcurrentLinkedQueue<String> messages = offlineMessages.get(this.erp);while (!messages.isEmpty()){sendMessage(messages.poll(),this.session);}}} catch (Exception e) {log.error("建立链接错误{}", e.getMessage(), e);}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {try {if (clients.containsKey(erp)) {clients.get(erp).session.close();clients.remove(erp);onlineCount.decrementAndGet();offlineMessages.computeIfAbsent(erp, k -> new ConcurrentLinkedQueue<>()).add("你不在的时候,南师很想你哦!!!");}log.info("有一连接关闭:{},当前在线人数为:{}", this.erp, onlineCount.get());} catch (Exception e) {log.error("连接关闭错误,错误原因{}", e.getMessage(), e);}}
在这边我们维护了一个map缓存存放某个用户断开连接期间其他用户发送的消息,等到用户再次上线时推送,这边我们目前是采用的缓存维护的方式,后续会做一个优化,考虑到如果该服务挂掉,那么缓存里的数据就会丢失,所以我们可以在这边对数据做一个持久化比如存储到数据库,服务挂掉之后重启的时候去表中读取未推送的数据加载到缓存中,这是需要优化的一个点;当然也可以考虑将数据存到redis里面配合redis的数据持久化机制可以保证数据不会丢失,但是需要考虑redis宕机的问题,这些不是本文讨论的重点,感兴趣的小伙伴可以根据可能出现的问题进行下一步思考。
然后是发送消息的逻辑,包括用户在线离线发送和群发消息的逻辑:
/*** 指定发送消息*/public void sendMessage(String message, Session session) {if(session!=null){log.info("服务端给客户端[{}]发送消息{}", this.erp, message);try {session.getBasicRemote().sendText(message);} catch (IOException e) {log.error("{}发送消息发生异常,异常原因{}", this.erp, message);}}}public void sendMessage(String message, String toId) {offlineMessages.computeIfAbsent(toId, k -> new ConcurrentLinkedQueue<>()).add(message);}/*** 群发消息*/public void sendMessage(String message) {for (Map.Entry<String, ChickenSocket> sessionEntry : clients.entrySet()) {String erp = sessionEntry.getKey();ChickenSocket socket = sessionEntry.getValue();Session session = socket.session;log.info("服务端给客户端[{}]发送消息{}", erp, message);try {session.getBasicRemote().sendText(message);} catch (IOException e) {log.error("{}发送消息发生异常,异常原因{}", this.erp, message);}}}
总结
在这里我们探讨了websocket的工作原理以及它在我们项目中的具体应用。除了消息回复的实时通知,它的应用也是非常广泛,比如说实时更新(股票行情,游戏数据更新),在线协作等等。相较于HTTP协议,websocket的通信模式是双向通信模式,而HTTP是请求响应模式,并且HTTP每次请求都会带上大量的请求头,websocket在建立连接之后只传输很少的头部信息。所以在不同的场景下我们考虑使用不同的协议,简单场景下HTTP已经足够,如果是双方消息通知或者实时推送的话可以考虑websocket协议。最后如果大家有什么想法,欢迎讨论。
相关文章:
双向通信之Websocket
介绍 Websocket是一种在单个TCP连接上进行全双工通信的协议。与传统的HTTP协议不同,websocket允许客户端与服务器之间的双向通信,可以在同一条连接上进行多次消息的快速传递。我之前在做一个线上刷题网站的时候,需要设计一个社区讨论模块&am…...
git学习使用碰到的问题1
本来在B站上看到的关于stash的使用时视频末尾讲到git stash drop 编号 会删除暂存记录 确实也是这么回事,但是末尾说到git stash pop 编号时up主说在恢复工作进度的时候我们可以直接删除掉这个工作记录可以直接使用 git stash pop stash{0} 使用完以后却出现了如上图…...
JavaScript初级——Math
一、Math 和其他的对象不同,它不是一个构造函数。它属于一个工具类,不用创建对象,里边封装了数学运算相关的属性和方法。 比如: Math.PI 表示圆周率。 二、Math.abs() —— 可以用来计算一个数的绝对值。 三…...
ffmpeg的基础命令
文章目录 ffmpeg/ffplay/ffprobe区别ffmpeg 的作用ffplay的作用ffprobe的作用 ffmpeg使用概述功能概述转码过程简单使用FFMPEG -i常用的 -i例子 ff***工具之间共享的选项ffmpeg主要选项ffmpeg提取音视频数据ffmpeg命令修改原有的视频格式ffmpeg命令裁剪和合并视频拼接视频的方式…...
二建机电工程实务试题内附答案
1.下列有色金属材料中,不属于铜合金的是()。 A.紫铜 B.青铜 C.黄铜 D.白铜【答案】A 2.用于完成介质间热量交换的换热设备是()。 A.分离器 B.反应器 C.冷凝器 D.分解锅【答案】C 3.工程测量的核心是()。 A.测量精度 B.设计要求 C.减少误差累积 D.检核【答案】D 4.吊…...
Redis的热key以及Big(大)key是什么?如何解决Redis的热key以及Big(大)key问题?
一、先讲讲什么是redis的热key问题 在Redis中,我们把访问频率高的Key,称为热Key。比如突然有几十万的请求去访问redis中某个特定的Key,那么这样会造成redis服务器短时间流量过于集中,很可能导致redis的服务器宕机。那么接下来对这…...
django学习入门系列之第九点《MySQL命令介绍一》
文章目录 MySQL命令数据库的管理(文件夹)查看现在已有的数据库(文件夹)创建数据库(文件夹)删除数据库(文件夹)进入数据库(文件夹)查看文件夹下所有的数据表&a…...
Mysql面试一
目录 一、事务的四大特性(ACID): 脏读 不可重复读 幻读 隔离性与隔离级别 数据库的三大范式 第一范式。确保数据表中的每个字段都是不可分割的最小单位,即原子性。这意味着表中的每一列都应代表一个独立的数据单元ÿ…...
模型优化之剪枝
文章目录 什么是神经网络剪枝剪枝的好处不同粒度的剪枝剪枝的分类非结构化剪枝结构化剪枝 哪些层的参数更容易被剪掉剪枝效果 什么是神经网络剪枝 神经网络剪枝 在训练期间删除连接密集张量将变得稀疏(用零填充)可以通过结构化块( n m nm nm&…...
JVM的组成
JVM 运行在操作系统之上 java二进制字节码文件的运行环境 JVM的组成部分 java代码在编写完成后编译成字节码文件通过类加载器 来到运行数据区,主要作用是加载字节码到内存 包含 方法区/元空间 堆 程序计数器,虚拟机栈,本地方法栈等等 随后来到执行引擎,主要作用是翻译字…...
快速上手 iOS Protocol Buffer
快速上手 iOS Protocol Buffer | 来自缤纷多彩的灰 本文主要介绍在 iOS 开发中如何快速上手使用 Protobuf。更多关于 Protobuf 的介绍和相关的功能 api,读者可自行查阅官网。 Protocol Buffer(简称 Protobuf)是一种由Google开发的语言中立、…...
每天一个数据分析题(四百八十)- 线性回归建模
关于线性回归建模,线性回归模型假设说法不正确的是? A. 因变量和自变量要有因果关系 B. 残差均值为0 C. 残差服从正态分布 D. 自变量不存在共线性 数据分析认证考试介绍:点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专…...
电动汽车和混动汽车DC-DC转换器的创新设计与测试方法
汽车 DC-DC 转换器市场规模将达到187亿美元,年复合增长率为10%。 DC-DC 转换器是汽车的重要组成部分,它可以通过电压转换为各种车载系统供电,例如日益复杂的车载信息娱乐系统、使用驾驶辅助系统(ADAS)实现的增强安全功…...
OriginPro快速上手指南:数据可视化与分析的利器
目录 OriginLab - Origin and OriginPro - Data Analysis and Graphing Softwarehttps://www.originlab.com/编辑 一、安装与界面概览 安装 界面概览 二、基础操作 数据输入 创建图表 三、高级功能 数据分析 自动化与脚本 Origin 提供了几个小工具 四、技巧与提示…...
缓存学习
缓存基本概念 概念 对于缓存,最普遍的理解是能让打开某些页面速度更快的工具。从技术角度来看,其本质上是因为缓存是基于内存建立的,而内存的读写速度相比之于硬盘快了xx倍,因此用内存来代替硬盘作为读写的介质当然能大大提高访…...
亚世光电:消费电子年度表演
机圈风云再起,消费电子乘风而起? 今天我们来聊——亚世光电 最近,华为mate60突然降价,被大家怀疑是为新品上市做准备,算算时间,下半年的消费电子大战也即将拉开帷幕,而亚世光电所在的光电显示领…...
AI 工程应用 建筑表面检测及修复
文章目录 1 项目概述(必写):2 技术方案与实施步骤2.1 模型选择(必写):2.2 数据的构建:2.3 功能整合(进阶): 3 实施步骤:3.1 环境搭建(…...
Qt-Qt中的小事项(7)
目录 命名风格 快捷键 查询文档 坐标系 代码理解 move 命名风格 这个也是老生常谈的问题了,入乡随俗就好啦 快捷键 这里是一些常用的快捷键,用多了自然就熟悉了 • 注释:ctrl/ • 运行:ctrlR • 编译:ctrlB …...
Android MediaRecorder 视频录制及报错解决
目录 一、start failed: -19 二、使用MediaRecorder录制视频 2.1 申请权限 2.2 布局文件 2.3 MediaRecordActivity 2.4 运行结果 三、拓展 3.1 录制视频模糊(解决) 3.2 阿里云OSS上传文件 3.2.1 权限(刚需) 3.2.2 安装SDK 3.2.3 使用 相关链接 一、start failed…...
HarmonyOS应用程序访问控制探究
关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 一、引言 随着信息技术的飞速发展,移动应用程序已经成为…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
