Spring Boot 集成 WebSocket 实现服务端推送消息到客户端
WebSocket 简介
WebSocket 协议是基于 TCP 的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信—允许服务器主动发送信息给客户端,这样就可以实现从客户端发送消息到服务器,而服务器又可以转发消息到客户端,这样就能够实现客户端之间的交互。对于 WebSocket 的开发,Spring 也提供了良好的支持,目前很多浏览器已经实现了 WebSocket 协议,但是依旧存在着很多浏览器没有实现该协议,为了兼容那些没有实现该协议的浏览器,往往还需要通过 STOMP 协议来完成这些兼容。
下面我们在 Spring Boot 中集成 WebSocket 来实现服务端推送消息到客户端。
Spring Boot 集成 WebSocket
首先创建一个 Spring Boot 项目,然后在 pom.xml
加入如下依赖集成 WebSocket:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
开启配置
接下来在 config 包下创建一个 WebSocket 配置类 WebSocketConfiguration,在配置类上加入注解 @EnableWebSocket,表明开启 WebSocket,内部实例化 ServerEndpointExporter 的 Bean,该 Bean 会自动注册 @ServerEndpoint 注解声明的端点,代码如下:
@Configuration
@EnableWebSocket
public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
编写端点服务类
接下来使用 @ServerEndpoint
定义一个端点服务类,在端点服务类中,可以定义 WebSocket 的打开、关闭、错误和发送消息的方法,具体代码如下所示:
package worn.xiao.riskmsg.service;import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);/*** 当前在线连接数*/private static AtomicInteger onlineCount = new AtomicInteger(0);/*** 用来存放每个客户端对应的 WebSocketServer 对象*/private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();/*** 与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 接收 userId*/private String userId = "";/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);webSocketMap.put(userId, this);} else {webSocketMap.put(userId, this);addOnlineCount();}log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());try {sendMessage("连接成功!");} catch (IOException e) {log.error("用户:" + userId + ",网络异常!!!!!!");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {if (webSocketMap.containsKey(userId)) {webSocketMap.remove(userId);subOnlineCount();}log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {log.info("用户消息:" + userId + ",报文:" + message);if (!StringUtils.isEmpty(message)) {try {JSONObject jsonObject = JSONObject.parseObject(message);jsonObject.put("fromUserId", this.userId);String toUserId = jsonObject.getString("toUserId");if (!StringUtils.isEmpty(toUserId) && webSocketMap.containsKey(toUserId)) {webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());} else {log.error("请求的 userId:" + toUserId + "不在该服务器上");}} catch (Exception e) {e.printStackTrace();}}}/*** 发生错误时调用** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());error.printStackTrace();}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}public static synchronized AtomicInteger getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount.getAndIncrement();}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount.getAndDecrement();}
}
其中,@ServerEndpoint("/websocket/{userId}")表示让 Spring 创建 WebSocket 的服务端点,其中请求地址是 /websocket/{userId}。
另外 WebSocket 一共有四个事件,分别对应定义的 @OnOpen、@OnMessage、@OnClose、@OnError 注解。
@OnOpen:标注客户端打开 WebSocket 服务端点调用方法
@OnClose:标注客户端关闭 WebSocket 服务端点调用方法
@OnMessage:标注客户端发送消息,WebSocket 服务端点调用方法
@OnError:标注客户端请求 WebSocket 服务端点发生异常调用方法
接下来启动项目,使用 WebSocket 在线测试工具(http://www.easyswoole.com/wstool.html)进行测试,有能力的也可以自己写个 html 测试。
测试html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Java后端WebSocket的Tomcat实现</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<div id="main" style="width: 1200px;height:800px;"></div>
Welcome<br/><input id="text" type="text" />
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window) {
//改成你的地址
websocket = new WebSocket("ws://192.168.100.196:8082/api/websocket/100");
} else {
alert('当前浏览器 Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function() {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function() {
setMessageInnerHTML("WebSocket连接成功");
}
var U01data, Uidata, Usdata
//接收到消息的回调方法
websocket.onmessage = function(event) {
console.log(event);
setMessageInnerHTML(event);
setechart()
}
//连接关闭的回调方法
websocket.onclose = function() {
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
websocket.send('{"msg":"' + message + '"}');
setMessageInnerHTML(message + " ");
}
</script>
</html>
打开网页后,在服务地址中输入ws://127.0.0.1:8088/websocket/xiaozhengwen,点击开启连接按钮,消息记录中会多一条由服务器端发送的连接成功!记录。
接下来再打开一个网页,服务地址中输入ws://127.0.0.1:8088/websocket/yangdandan,点击开启连接按钮,然后回到第一次打开的网页在消息框中输入
应用场景
1,统计在线人数
2,后台页面向客户端发送主动发送数据,支持单发和群发
相关文章:

Spring Boot 集成 WebSocket 实现服务端推送消息到客户端
WebSocket 简介 WebSocket 协议是基于 TCP 的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信—允许服务器主动发送信息给客户端,这样就可以实现从客户端发送消息到服务器,而服务器又可以转发消息到客…...

vr游乐场项目投资方案VR主题游乐馆互动体验
VR文旅景区沉浸互动体验项目是指利用虚拟现实技术在文旅景区中创建沉浸式的互动体验项目。通过虚拟现实技术,游客可以身临其境地体验景区的风景和文化,与虚拟场景中的元素进行互动。 普乐蛙VR设备 普乐蛙VR设备案例分享 这种项目可以为游客带来全新的旅游…...
chrom扩展开发配合百度图像文字识别实现自动登录(后端.net core web api)
好久没做浏览器插件开发了,因为公司堡垒机,每次登录都要输入账号密码和验证码。太浪费时间了,就想着做一个右键菜单形式的扩展。 实现思路也很简单,在这里做下记录,方便下次开发参考。 一,先来了解下chro…...

香港服务器怎么打开SSH
SSH是一种远程登录协议,可以通过加密方式在网络上安全地传输数据。它允许用户在远程服务器上执行命令,管理文件和目录,并进行其他系统管理任务。 如何打开SSH服务? 1.确认已安装OpenSSH服务器: 你可以通过命令sudoapt-geti…...

【LeetCode】437.路径总和Ⅲ
题目 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节…...
Mybatis-plus中操作JSON字段
1.实体类上要加上自动映射 TableName(value "school", autoResultMap true)2.json字段上加上json处理器 TableField(value "cover_url", typeHandler JacksonTypeHandler.class)private List<String> cover_url;参考博客 http://www.dedeyun.co…...

第十五课、Windows 下打包发布 Qt 应用程序
功能描述:讲解了 Windows 下打包发布 Qt 应用程序的三种方法,并对比优缺点 一、利用 windepolyqt 工具打包发布 Qt 提供了一个 windeployqt 工具来自动创建可部署的文件夹。 打包发布流程: 1. 新建一个文件夹,将编译后的可执行…...

【php】windows下php运行已有php web项目环境配置教程
php环境配置教程 php安装composer安装扩展安装redis扩展安装 composer install 本文操作系统使用的是win11,软件PhpStorm 2023.1 php安装 要安装的php版本可以在composer.json看到,下载安装对应版本 windows下载地址https://windows.php.net/download …...
【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间...
参考链接 【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间…...

C++动态规划经典案例解析之合并石子
1. 前言 区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如前缀和就是极简单的区间问题。如有如下数组: int nums[]{3,1,7,9,12,78,32,5,10,11,21,32,45,22}现给定区间信息[…...
go MongoDB
安装 go get go.mongodb.org/mongo-driver/mongo package mongodbexampleimport ("context""fmt""ginapi/structs""time""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""…...
算法与数据结构(八)--优先队列
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除,在某些情况下,我们可能需要找出队列中的最大值或者最小值。 例如使用一个队列保存计算机的任务,一般情况下计算机的任务都是有优先级的ÿ…...

React 全栈体系(三)
第二章 React面向组件编程 四、组件三大核心属性3: refs与事件处理 1. 效果 需求: 自定义组件, 功能说明如下: 点击按钮, 提示第一个输入框中的值当第2个输入框失去焦点时, 提示这个输入框中的值 2. 理解 组件内的标签可以定义ref属性来标识自己 3. 编码 3.1 字符串形式…...

腾讯云下一代CDN -- EdgeOne加速MinIO对象存储
省流 使用MinIO作为EdgeOne的源站。 背景介绍 项目中需要一个兼容S3协议的对象存储服务,腾讯云的COS虽然也兼容S3协议,但是也只是支持简单的上传下载,对于上传的时候同时打标签这种需求,就不兼容S3了。所以决定自建一个对象存储…...

GitLab-CI 指南
GitLab CI 指南 前置工作 部署GitLab 部署GitLab-Runner 注册Runner到GitLab docker exec -it gitlab-runner bash # 进入容器 gitlab-runner register #调用register命令开始注册 # 在Gitlab Setting中找到Runners,如下图所示Enter the GitLab instance URL (for example, …...

MyBatis的核心技术掌握,简单易懂(上)
目录 一.MyBatis中的动态SQL 二.MyBatis中的模糊查询 1. # 符号 2. $ 符号 ---问题 ---所以大家知道 # 和 $ 在MyBatis中的模糊查询中的区别了嘛?? 三.MyBatis 中的结果映射 1. resultType: 2. resultMap: ---问题 ---…...
Redisson自定义序列化
Redisson自定义序列化_redisson 序列化_yzh_1346983557的博客-CSDN博客 redis存取的数据一定是可序列化的,而可序列化方式可以自定义。如果不同客户端设置的可序列化方式不一样,会导致读取不一致的问题。常见的序列化方式有几下几种...

MongoDB Long 类型 shell 查询
场景 1、某数据ID为Long类型,JAVA 定义实体类 Id Long id 2、查询数据库,此数据存在 3、使用 shell 查询,查不到数据 4、JAVA代码查询Query.query 不受任何影响 分析 尝试解决(一) long 在 mongo中为 int64 类型…...

回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测
回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-…...

Spring cache整合Redis使用介绍
🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

在 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…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...