《知识点扫盲 · 学会 WebSocket》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 长连接 WebSocket
      • 技术简介
      • 四个事件
      • SpringBoot 整合 WS
      • 前端 JS 实现 WS
    • 总结陈词

CSDN.gif

写在前面的话

本系列的上篇文章《知识点扫盲 · 学会 WebService》介绍了企业开发中WebService技术的实际应用,这边继续介绍一下WebSocket的基础应用,希望可以帮助到大家。
这里先介绍实战运用,深入的部分后续专题介绍,让我们开始!

Tips:WebSocket,总感觉名字和 WebService 怎么那么像?WS又算谁的缩写呢?


长连接 WebSocket

技术简介

1、WebSocket 是 HTML5 开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。
2、WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
3、WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
4、虽然前后端都可以互相推数据,但主要还是后端推送给前端,常见运用场景:实时聊天、通知公告、视频弹幕。

Tips:总结一句话,保持长连接,让前后端可以互相交互,畅通无阻。

四个事件

open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

Tips:混个眼熟。

SpringBoot 整合 WS

Step1、引入依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

Step2、添加配置类

@Configuration
public class WebSocketConfig {/*** 	注入ServerEndpointExporter,* 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

Step3、添加具体Socket服务

@Component
@Slf4j
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketHandle {/*** 与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 用户ID*/private String userId;/*** concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。* 虽然@Component默认是单例模式的,但SB还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。*/private static final CopyOnWriteArraySet<WebSocketHandle> WEB_SOCKETS = new CopyOnWriteArraySet<>();/*** 用来存在线连接用户信息*/private static final ConcurrentHashMap<String, Session> SESSION_POOL = new ConcurrentHashMap<>();@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}WebSocketHandle that = (WebSocketHandle) o;return Objects.equals(session, that.session) && Objects.equals(userId, that.userId);}@Overridepublic int hashCode() {return Objects.hash(session, userId);}/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "userId") String userId) {//初始化设置当前实例的信息this.session = session;this.userId = userId;//加入全局Socket管理(Set)WEB_SOCKETS.add(this);//加入全局session管理(Map)SESSION_POOL.put(userId, session);log.info("WebSocket有新的连接,用户为:{}, 总数为:{}", userId, WEB_SOCKETS.size());}/*** 链接关闭调用的方法*/@OnClosepublic void onClose(Session session) {WEB_SOCKETS.remove(this);SESSION_POOL.remove(this.userId);log.info("WebSocket有连接断开,用户为:{}, 总数为:{}", userId, WEB_SOCKETS.size());}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(Session session, String message) {log.info("WebSocket收到客户端消息,用户为:{}, 消息为:{}:", this.userId, message);}/*** 发送错误时的处理*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:" + error.getMessage());error.printStackTrace();}/*** 发消息给全部人*/public void sendAllMessage(String message) {log.info("【websocket消息】广播消息:" + message);for (WebSocketHandle webSocket : WEB_SOCKETS) {try {if (webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}/*** 发消息给单个人*/public void sendOneMessage(String userId, String message) {Session session = SESSION_POOL.get(userId);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}/*** 发消息给多个人*/public void sendMoreMessage(String[] userIds, String message) {for (String userId : userIds) {Session session = SESSION_POOL.get(userId);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}
}

Step4、测试服务端效果
参考:WebSocket在线测试
按上述步骤改造完,就可以得到WS的服务端,地址形如:ws://127.0.0.1:8180/webSocket/123
如果想不写客户端,可以直接使用测试网站进行测试,如下图。
image.png

Step5、测试发送消息
随便写一个接口,就可以触发调用了,看看客户端是否有接收到。
注意,这里的webSocketHandle确实是单例的,但是单个Socket连接也是存在的,而且Bean实例里面的static类型的Map和Set还是存了后续想要操作的内容。

@RequestMapping("/socketTest")
public void socketTest(String id) throws Exception {webSocketHandle.sendOneMessage(id, "hello~");
}

前端 JS 实现 WS

前端使用WebSocket,可以用原生的方式,参考如下。
当然,也可以引用第三方库,比较出名的是socket.io。

/*** 备忘* 页面地址:http://localhost:8180/test/socketDemo.html* 后端发消息:http://localhost:8180/socketTest?id=123*/if ("WebSocket" in window) {let $scope = {}let wsUrl = "ws://127.0.0.1:8180/webSocket/123"let ws;let tt;let lockReconnect = false;// 创建WS链接$scope.createWebSocket = function (wsUrl) {try {ws = new WebSocket(wsUrl);$scope.webSocketInit();} catch (e) {lockReconnect = false$scope.webSocketReconnect(wsUrl)//重连函数}};// 初始化WS的方法$scope.webSocketInit = function () {ws.onclose = function (error) {//连接关闭的回调函数,进行重连console.log("连接已关闭...", error);$scope.webSocketReconnect(wsUrl)};ws.onerror = function (error) {//连接错误的回调函数,进行重连console.log("连接错误...", error);$scope.webSocketReconnect(wsUrl)};ws.onopen = function () {//连接建立//发一个初始化连接消息ws.send('初始化连接');//启动心跳检测$scope.heartCheck.start();};ws.onmessage = function (event) {if(event.data !== 'pong'){let $test = $('#textA')let temp = $test.text();$test.text(temp + event.data + "\r\n");console.log("收到后端的消息:", event.data);} else {console.log('收到pong消息,连接还正常~')}//接收一次后台推送的消息,即进行一次心跳检测重置$scope.heartCheck.reset();};};$scope.webSocketReconnect = function (url) {console.log("socket 连接断开,正在尝试重新建立连接");//TODO 下面这段代码会导致只重连一次,后续改进了再开放//TODO 长时间如果没收到pong消息应该也要处理,提示一下报错之类的/*if (lockReconnect) {return;}lockReconnect = true;*///没连接上会一直重连,设置延迟,避免请求过多tt && clearTimeout(tt);tt = setTimeout(function () {$scope.createWebSocket(url);}, 4000)};//心跳检测//onopen连接上,就开始start及时,如果在定时时间范围内,onmessage获取到了服务端消息,就重置reset倒计时,距离上次从后端获取消息30秒后,执行心跳检测,看是不是断了。$scope.heartCheck = {timeout: 5000, //默认30秒timeoutObj: null,reset: function () { //接收成功一次推送,就将心跳检测的倒计时重置为30秒clearTimeout(this.timeoutObj);//重置倒计时this.start();},start: function () {//启动心跳检测机制,设置倒计时30秒一次this.timeoutObj = setTimeout(function () {//启动心跳ws.send("ping");}, this.timeout)}};//开始创建webSocket连接$scope.createWebSocket(wsUrl);// 点击发消息给后端,后端收到后回复信息function sendMessage() {let message = document.getElementById("messageInput").value;if (message) {ws.send(message);}}} else {// 浏览器不支持 WebSocketalert("您的浏览器不支持 WebSocket!");
}

总结陈词

此篇文章介绍了WebSocket的基础应用,仅供学习参考。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

笔记 5 :linux 0.11 注释,函数 copy_mem() , copy_process () , 中断函数 int 80H 的代码框架

&#xff08;38&#xff09;接着介绍一个创建进程时的重要的函数 copy_mem&#xff08;&#xff09; 函数&#xff1a; &#xff08;39&#xff09; 分析另一个关于 fork&#xff08;&#xff09; 的重要的函数 copy_process&#xff08;&#xff09;&#xff0c;与李忠老师的操…

大模型的“幻觉”克星!被低估的RAG技术

1 RAG与大模型、Prompt、微调的关系 本文主要带大家深入学习一下最近AI领域的重要技术RAG&#xff0c;本文致力于用大白话给大家说明白RAG&#xff0c;但是还是需要一些大模型和微调有关的领域名词有一些基本的了解&#xff0c;大家选择性阅读哦!在进行正文学习之前我们先用一…

【内网穿透】打洞笔记

文章目录 前言原理阐述公网sshfrp转发服务 实现前提第一步&#xff1a;第二步第三步第四步 补充第五步&#xff08;希望隧道一直开着&#xff09;sftp传数据&#xff08;嫌云服务器上的网太慢&#xff09; 前言 租了一个云服务器&#xff0c;想用vscode的ssh远程连接&#xff…

数据库中的复合查询

一、基本查询回顾 1、查询工资高于500或岗位为manager的员工&#xff0c;并且名字首字母是J 2、按部门编号升序&#xff0c;工资降序排序 3、用年薪降序排序 4、显示工资最高的员工名字和岗位&#xff08;使用了子查询&#xff09; 5、查询工资高于平均工资的员工 6、按部门查…

kubernetes集群部署elasticsearch集群,包含无认证和有认证模式

1、背景&#xff1a; 因公司业务需要&#xff0c;需要在测试、生产kubernetes集群中部署elasticsearch集群&#xff0c;因不同环境要求&#xff0c;需要部署不同模式的elasticsearch集群&#xff0c; 1、测试环境因安全性要求不高&#xff0c;是部署一套默认配置&#xff1b; 2…

orcad导出pdf 缺少title block

在OrCAD中导出PDF时没有Title Block 最后确认问题在这里&#xff1a; 要勾选上Title Block Visible下面的print

《昇思25天学习打卡营第19天|Diffusion扩散模型》

什么是Diffusion Model&#xff1f; 什么是Diffusion Model? 如果将Diffusion与其他生成模型&#xff08;如Normalizing Flows、GAN或VAE&#xff09;进行比较&#xff0c;它并没有那么复杂&#xff0c;它们都将噪声从一些简单分布转换为数据样本&#xff0c;Diffusion也是从…

46 mysql 客户端拿不到具体的错误信息

前言 这是最近碰到的一个问题 同样的一个 环境的问题, 在正常的 mysql 环境会返回 具体的错误信息, 然后 在我的另外一个环境里面 只能返回一些 unknown error 之类的 十分抽象的环境 然后 我们这里 来看一下 具体的情况 我们这里从 错误的环境 往前推导 来查看 并解决这个…

常见的计算机语言有哪些?

计算机语言用于编写软件和应用程序&#xff0c;各种语言有不同的用途和特点。目前常见的的编程语言有&#xff1a; 1、Java&#xff1b;2、Python&#xff1b;3、JavaScript&#xff1b;4、C&#xff1b;5、C#&#xff1b;6、Swift&#xff1b;7、Go&#xff1b;8、Ruby&#…

Chromium CI/CD 之Jenkins实用指南2024-系统基本配置(二)

1. 引言 在现代软件开发和持续集成/持续部署&#xff08;CI/CD&#xff09;过程中&#xff0c;Jenkins已经成为一款不可或缺的工具。作为一个开源的自动化服务器&#xff0c;Jenkins能够帮助开发团队自动化各种任务&#xff0c;包括构建、测试和部署应用程序。特别是当与Ubunt…

高职综合布线实训室

一、高职综合布线实训室建设背景 随着《国民经济和社会发展第十四个五年规划和2035年远景目标纲要》的深入实施&#xff0c;数字化转型已成为国家发展的核心战略之一&#xff0c;计算机网络技术作为数字化建设的基石&#xff0c;其重要性日益凸显。然而&#xff0c;面对数字时代…

数据可视化在智慧医疗中的重要应用

在现代智慧医疗的推动下&#xff0c;数据可视化技术正日益成为医疗领域的重要工具。通过将复杂的医疗数据转换为直观的图表和图形&#xff0c;数据可视化不仅提升了医疗服务的效率&#xff0c;还极大地改善了患者的就医体验。 在智慧医疗中&#xff0c;数据可视化首先在电子病历…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 7月17日,星期三

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年7月17日 星期三 农历六月十二 1、 三部门&#xff1a;紧急调拨1万件救灾物资支持河南、山东两省做好受灾群众基本生活保障工作。 2、 多部门联合公布无人驾驶试点名单&#xff1a;北京、上海等20个城市&#xff08;联合体…

Cesium能做啥,加载哪些数据源,开源免费用商用吗?这里告诉你。

很多小伙伴对Cesium是什么&#xff0c;一知半解&#xff0c;本文是基础知识的扫盲&#xff0c;为大家分享cesium是什么、能做什么、默认数据是什么&#xff0c;为什么首先要进行数据加载&#xff0c;要加载哪些数据&#xff0c;希望通过这些带你入个门&#xff0c;欢迎点赞评论…

所有权与生命周期:Rust 内存管理的哲学

所有权与生命周期&#xff1a;Rust内存管理的哲学 博主寄语引言&#xff1a;编程语言的内存管理困境与 Rust 的解决方案。所有权基本概念&#xff1a;资源的绝对主权生命周期的理解与应用&#xff1a;编译时的守护神借用与引用的精妙设计&#xff1a;安全与效率的和谐共舞Rust …

服务器基础1

服务器基础复习01 1.环境部署 系统&#xff1a;华为欧拉系统 网络简单配置nmtui 因为华为欧拉系统密码需要复杂度 所以我们可以进入后更改密码 echo 123 | passwd --stdin root也可以 echo "root:123" | chpasswd2.关闭防火墙&#xff0c;禁用SElinux 首先先关…

C++:链表插入排序/删除重复节点题解

插入排序 插入排序的思路很简单&#xff0c;基本都知道。 关键是放在链表中&#xff0c; 1.要建立一个哨兵位&#xff0c;这个哨兵位的下一个节点&#xff0c;始终指向val最小的节点。 2.prev指针作为cur的前一个节点&#xff0c;始终指向val最大的节点。它的下一个节点始终…

C++ ─── vector的模拟实现

知识点&#xff1a; ① 因为vector是模版&#xff0c;所以声明和定义都放在.h中&#xff0c;防止出现编译错误。 .h不会被编译&#xff0c;在预处理中.h在.cpp中展开&#xff0c;所以在编译时只有.cpp 而 .cpp顺序编译&#xff0c;只会进行向上查找&#xff0c;因此至少有函数的…

交叉编译ethtool(ubuntu 2018)

参考文章&#xff1a;https://www.cnblogs.com/nazhen/p/16800427.html https://blog.csdn.net/weixin_43128044/article/details/137953913 1、下载相关安装包 //ethtool依赖libmul git clone http://git.netfilter.org/libmnl //ethtool源码 git clone http://git.kernel.or…

野兔在线工具箱系统全新升级改版,基于TP8和yetuadmin后台实现

野兔在线工具箱系统全新升级改版&#xff0c;基于TP8和yetuadmin后台实现 系统名称&#xff1a;野兔在线工具系统 系统语言&#xff1a;支持多语言&#xff0c;大概有20种 系统源码&#xff1a;不加密&#xff0c;开源 系统开发&#xff1a;PHPMySQL (基于thinkphp8&#x…