当前位置: 首页 > news >正文

Springboot 集成 WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

1.添加依赖包

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

2.添加WebSocket配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

3.编写web服务端

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;@Component
@ServerEndpoint("/websocket/{clientId}")
@Slf4j
public class WebSocketServer {/*** 客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 客户端id*/private String clientId;/*** 用来存放每个客户端对应的MyWebSocket对象*/private static CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();/*** 用来存在线连接用户信息*/private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "clientId") String clientId) {try {this.session = session;this.clientId = clientId;webSockets.add(this);sessionPool.put(clientId, session);log.info("【websocket消息】有新的连接 clientId:{},总数为:{}", clientId, webSockets.size());} catch (Exception e) {log.info("【websocket消息】有新的连接构建失败 clientId:{},总数为:{},error:", clientId, webSockets.size(), e);}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.clientId);log.info("【websocket消息】连接断开 clientId:{},总数为:{}", this.clientId, webSockets.size());} catch (Exception e) {log.info("【websocket消息】连接断开失败 clientId:{},总数为:{},error:", clientId, webSockets.size(), e);}}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:{}", message);}/*** 发送错误时的处理** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("【websocket消息】发生错误,原因:", error);}/*** 此为广播消息*/public void sendBroadcastMessage(String message) {log.info("【websocket消息】广播消息:{}", message);for (WebSocketServer webSocket : webSockets) {try {if (webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {log.error("【websocket消息】广播消息异常,消息:{},error:", message, e);}}}/*** 此为单点消息*/public void sendSinglePointMessage(String targetId, String message) {Session session = sessionPool.get(targetId);if (session != null && session.isOpen()) {try {session.getAsyncRemote().sendText(message);log.info("【websocket消息】单点消息,targetId:{},消息:{}", targetId, message);} catch (Exception e) {log.error("【websocket消息】单点消息异常,targetId:{},消息:{},error:", targetId, message, e);}}}}

4.html测试页面

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Websocket客户端</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>var socket;function openSocket() {const socketUrl = "ws://localhost:8081/websocket/" + $("#clientId").val();console.log(socketUrl);if (socket != null) {socket.close();socket = null;}socket = new WebSocket(socketUrl);// 开启WebSocket连接socket.onopen = function () {console.log("websocket已开启");};// 获得消息事件socket.onmessage = function (msg) {console.log(msg.data);};// 关闭事件socket.onclose = function () {console.log("websocket已关闭");};// 发生了错误事件socket.onerror = function () {console.log("websocket发生了错误");}}function sendMessage() {socket.send('{"targetId":"' + $("#targetId").val() + '","contentText":"' + $("#contentText").val() + '"}');console.log('{"targetId":"' + $("#targetId").val() + '","contentText":"' + $("#contentText").val() + '"}');}
</script>
<body><div>客户端身份标识ID<input id="clientId" type="text" value="10"><button style="color: cornflowerblue"><a onclick="openSocket()">开启WebSocket连接</a></button>
</div>
<br><br>
<div>客户端向服务器发送的内容<input id="targetId" type="text" value="20"><input id="contentText" type="text" value="hello WebSocket"><button style="color: cornflowerblue"><a onclick="sendMessage()">发送消息</a></button>
</div></body>
</html>

5.模拟服务端发送消息

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;@Component
@ServerEndpoint("/websocket/{clientId}")
@Slf4j
public class WebSocketServer {/*** 客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 客户端id*/private String clientId;/*** 用来存放每个客户端对应的MyWebSocket对象*/private static CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();/*** 用来存在线连接用户信息*/private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "clientId") String clientId) {try {this.session = session;this.clientId = clientId;webSockets.add(this);sessionPool.put(clientId, session);log.info("【websocket消息】有新的连接 clientId:{},总数为:{}", clientId, webSockets.size());} catch (Exception e) {log.info("【websocket消息】有新的连接构建失败 clientId:{},总数为:{},error:", clientId, webSockets.size(), e);}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.clientId);log.info("【websocket消息】连接断开 clientId:{},总数为:{}", this.clientId, webSockets.size());} catch (Exception e) {log.info("【websocket消息】连接断开失败 clientId:{},总数为:{},error:", clientId, webSockets.size(), e);}}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:{}", message);}/*** 发送错误时的处理** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("【websocket消息】发生错误,原因:", error);}/*** 此为广播消息*/public void sendBroadcastMessage(String message) {log.info("【websocket消息】广播消息:{}", message);for (WebSocketServer webSocket : webSockets) {try {if (webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {log.error("【websocket消息】广播消息异常,消息:{},error:", message, e);}}}/*** 此为单点消息*/public void sendSinglePointMessage(String targetId, String message) {Session session = sessionPool.get(targetId);if (session != null && session.isOpen()) {try {session.getAsyncRemote().sendText(message);log.info("【websocket消息】单点消息,targetId:{},消息:{}", targetId, message);} catch (Exception e) {log.error("【websocket消息】单点消息异常,targetId:{},消息:{},error:", targetId, message, e);}}}}

发送广播消息:http://localhost:8081/api//websocket/sendBroadcastMessage?message=hello

发送点对点消息:http://localhost:8081/api//websocket/sendSinglePointMessage?message=hello&targetId=10

相关文章:

Springboot 集成 WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接…...

谨以此篇,纪念我2023年曲折的计算机保研之路

目录 阶段一&#xff1a;迷茫阶段二&#xff1a;准备个人意愿保研材料准备套磁老师5.1日 浙大线上编程测试5.8日 浙大线上面试 —— 一面5.17日 浙大线上面试——二面5.29日 实验室面试结果5.27日 南开线上面试6.20日 华师电话面试 阶段三&#xff1a;旅途北航CS&#xff08;6.…...

VSS、VDD、VBAT、VSSA

引言 在学习设计TM32时&#xff0c;发现芯片除了GPIO引脚外还会引出许多引脚&#xff0c;以STM32F407ZGT6为例除了GPIO引脚还会有以下引脚 如VSS、VDD、VBAT、VSSA、NRST、VREF、VDDA、VCAP_1、VCAP_2、PDR_ON这些引脚。他们有何作用&#xff0c;电路设计中应如何连接&#x…...

【Rust基础③】方法method、泛型与特征

文章目录 6 方法 Method6.1 定义方法self、&self 和 &mut self 6.2 自动引用和解引用6.3 关联函数 7 泛型和特征7.1 泛型 Generics7.1.1 结构体中使用泛型7.1.2 枚举中使用泛型7.1.3 方法中使用泛型为具体的泛型类型实现方法 7.1.4 const 泛型 7.2 特征 Trait7.2.1 为类…...

48.排列问题求解

思路分析&#xff1a;通过为每一队分配一个id&#xff0c;join条件要求t1.num < t2.num实现相同两队只比一次 代码实现&#xff1a; with t as (SELECT team_name,caseteam_nameWHEN 勇士 then 1WHEN 湖人 then 2WHEN 灰熊 then 3else 4end numFROM team )SELECT t1.team_…...

18.(开发工具篇Gitlab)Git如何回退到指定版本

首先: 使用git log命令查看提交历史,找到想要回退的版本的commit id. 使用git reset命令 第一步:git reset --hard 命令是强制回到某一个版本。执行后本地工程回退到该版本。 第二步:利用git push -f命令强制推到远程 如下所示: 优点:干净利落,回滚后完全回到最初状态…...

IDEA初始配置

1. 详细设置 安装完IDEA之后的简单配置。 1.1 如何打开详细配置界面 1、显示工具栏 2、选择详细配置菜单或按钮 1.2 系统设置 1、默认启动项目配置 启动IDEA时&#xff0c;默认自动打开上次开发的项目&#xff1f;还是自己选择&#xff1f; 如果去掉Reopen projects on …...

WM_COPYDATA传回返回值的一个方案

方案背景 适应场景&#xff0c;通过WM_COPYDATA进行进程间通信时&#xff0c;SendMessage不能返回自定义的数据&#xff0c;由此想到以下思路解决这个问题 A进程使用VirtualAlloc分配一块内存&#xff0c;通过某种方式将此地址以及A进程ID传给另一个进程B B进程使用OpenProce…...

【日常业务开发】接口性能优化

【日常业务开发】接口性能优化 缓存本地缓存分布式缓存 数据库分库分表SQL 优化 业务程序并行化异步化池化技术预先计算事务粒度批量读写锁的粒度尽快return上下文传递空间换时间集合空间大小 缓存 本地缓存 本地缓存&#xff0c;最大的优点是应用和cache同一个进程内部&…...

Android 10.0 禁止弹出系统simlock的锁卡弹窗功能实现

1.前言 在10.0的系统开发中,在一款产品中,需要实现simlock锁卡功能,在系统实现锁卡功能以后,在开机的过程中,或者是在插入sim卡 后,当系统检测到是禁用的sim卡后,就会弹出simlock锁卡弹窗,要求输入puk 解锁密码,功能需求禁用这个弹窗,所以就需要看是 哪里弹的,禁用…...

VulnHub lazysysadmin

一、信息收集 1.nmap扫描开发端口 开放了&#xff1a;22、80、445 访问80端口&#xff0c;没有发现什么有价值的信息 2.扫描共享文件 enum4linux--扫描共享文件 使用&#xff1a; enum4linux 192.168.103.182windows访问共享文件 \\192.168.103.182\文件夹名称信息收集&…...

ppt怎么压缩到10m以内?分享ppt缩小方法

在日常工作中&#xff0c;我们常常需要制作和分享PowerPoint演示文稿&#xff0c;然而&#xff0c;有时候文稿中的图片、视频等元素会导致文件过大&#xff0c;无法在电子邮件或其他平台上顺利传输。为了将PPT文件压缩到10M以内&#xff0c;我们可以使用一些专门的文件压缩工具…...

智能警用装备管理系统-科技赋能警务

警用物资装备管理系统&#xff08;智装备DW-S304&#xff09;是依托互云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对警用装备进行统一管理、分析的信息化、智能化、规范化的系统。 &#xff08;1&#xff09;感知智能化 装备感知是整个方案的基础&#xff0c;本方…...

攻防千层饼

近年来&#xff0c;网络安全领域正在经历一场不断升级的攻防对抗&#xff0c;这场攻防已经不再局限于传统的攻击与防御模式&#xff0c;攻击者和防守者都已经越发熟练&#xff0c;对于传统攻防手法了如指掌。 在这个背景下&#xff0c;攻击者必须不断寻求创新的途径&#xff0…...

组件封装使用?

组件封装是指在软件开发中&#xff0c;将功能代码或数据封装成一个独立的、可重用的模块或组件。这种封装可以使得代码更加模块化、可维护性和可重用性。在许多编程语言和开发框架中&#xff0c;都有不同的方式来实现组件封装。 以下是一些常见的组件封装方法和技巧&#xff1…...

2.3 初探Hadoop世界

文章目录 零、学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;Hadoop的前世今生1、Google处理大数据三大技术2、Hadoop如何诞生3、Hadoop主要发展历程 &#xff08;二&#xff09;Hadoop的优势1、扩容能力强2、成本低3、高效率4、可靠性5、高容错性 &#xff08;三…...

Flutter笔记:发布一个电商中文货币显示插件Money Display

Flutter笔记 电商中文货币显示插件 Money Display 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1338…...

解密zkLogin:探索前沿的Sui身份验证解决方案

由于钱包复杂性导致的新用户入门障碍是区块链中一个长期存在的问题&#xff0c;而zkLogin是其简单的解决方案。通过使用前沿的密码学和技术&#xff0c;zkLogin既优雅又复杂。本文深入探讨了zkLogin的工作原理&#xff0c;涵盖了用户和开发者的安全性方面&#xff0c;并解释了S…...

js构造函数

构造函数 通过 new 函数名 来实例化对象的函数叫构造函数。 任何的函数都可以作为构造函数存在。之所以有构造函数与普通函数之分&#xff0c;主要从功能上进行区别的&#xff0c;构造函数的主要 功能为 初始化对象&#xff0c;特点是和new 一起使用。new就是在创建对象&#x…...

性能测试-redis常见问题

缓存击穿、缓存穿透、缓存雪崩 缓存雪崩 解决办法 1.设置缓存失效时间&#xff0c;不要在同一时间 2.redis集群部署 3.不设置缓存设置时间 4.定时刷缓存的时间 缓存穿透 请求不管返回什么数据都返回给redis对参数合法器进行验证&#xff0c;不合法的时候直接过滤掉使用布…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...