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

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 + "&#13;");
}
</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 的一种新的网络协议&#xff0c;它实现了浏览器与服务器全双工&#xff08;full-duplex&#xff09;通信—允许服务器主动发送信息给客户端&#xff0c;这样就可以实现从客户端发送消息到服务器&#xff0c;而服务器又可以转发消息到客…...

vr游乐场项目投资方案VR主题游乐馆互动体验

VR文旅景区沉浸互动体验项目是指利用虚拟现实技术在文旅景区中创建沉浸式的互动体验项目。通过虚拟现实技术&#xff0c;游客可以身临其境地体验景区的风景和文化&#xff0c;与虚拟场景中的元素进行互动。 普乐蛙VR设备 普乐蛙VR设备案例分享 这种项目可以为游客带来全新的旅游…...

chrom扩展开发配合百度图像文字识别实现自动登录(后端.net core web api)

好久没做浏览器插件开发了&#xff0c;因为公司堡垒机&#xff0c;每次登录都要输入账号密码和验证码。太浪费时间了&#xff0c;就想着做一个右键菜单形式的扩展。 实现思路也很简单&#xff0c;在这里做下记录&#xff0c;方便下次开发参考。 一&#xff0c;先来了解下chro…...

香港服务器怎么打开SSH

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

【LeetCode】437.路径总和Ⅲ

题目 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到子节…...

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 应用程序

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

【php】windows下php运行已有php web项目环境配置教程

php环境配置教程 php安装composer安装扩展安装redis扩展安装 composer install 本文操作系统使用的是win11&#xff0c;软件PhpStorm 2023.1 php安装 要安装的php版本可以在composer.json看到&#xff0c;下载安装对应版本 windows下载地址https://windows.php.net/download …...

【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间...

参考链接 【mybatis】 mybatis在mysql 更新update 操作 更新时间字段按照年月日时分秒格式 更新为当前时间…...

C++动态规划经典案例解析之合并石子

1. 前言 区间类型问题&#xff0c;指求一个数列中某一段区间的值&#xff0c;包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如前缀和就是极简单的区间问题。如有如下数组&#xff1a; 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""…...

算法与数据结构(八)--优先队列

普通的队列是一种先进先出的数据结构&#xff0c;元素在队列尾追加&#xff0c;而从队列头删除&#xff0c;在某些情况下&#xff0c;我们可能需要找出队列中的最大值或者最小值。 例如使用一个队列保存计算机的任务&#xff0c;一般情况下计算机的任务都是有优先级的&#xff…...

React 全栈体系(三)

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

腾讯云下一代CDN -- EdgeOne加速MinIO对象存储

省流 使用MinIO作为EdgeOne的源站。 背景介绍 项目中需要一个兼容S3协议的对象存储服务&#xff0c;腾讯云的COS虽然也兼容S3协议&#xff0c;但是也只是支持简单的上传下载&#xff0c;对于上传的时候同时打标签这种需求&#xff0c;就不兼容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中的模糊查询中的区别了嘛&#xff1f;&#xff1f; 三.MyBatis 中的结果映射 1. resultType&#xff1a; 2. resultMap&#xff1a; ---问题 ---…...

Redisson自定义序列化

Redisson自定义序列化_redisson 序列化_yzh_1346983557的博客-CSDN博客 redis存取的数据一定是可序列化的&#xff0c;而可序列化方式可以自定义。如果不同客户端设置的可序列化方式不一样&#xff0c;会导致读取不一致的问题。常见的序列化方式有几下几种...

MongoDB Long 类型 shell 查询

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

回归预测 | MATLAB实现GA-APSO-IBP改进遗传-粒子群算法优化双层BP神经网络多输入单输出回归预测

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

Spring cache整合Redis使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...