【stomp实战】Springboot+Stomp协议实现聊天功能

本示例实现一个功能,前端通过websocket发送消息给后端服务,后端服务接收到该消息时,原样将消息返回给前端。前端技术栈html+stomp.js,后端SpringBoot

前端代码

关于stomp客户端的开发,如果不清楚的,可以看一下上一篇文章
这里我对Stomp.js进行了一个简单的封装,写在stomp-client.js里面

/*** 对 stomp 客户端进行封装*/var client;
var subscribes = [];
var errorTimes = 0;var endpoint = "/ws";/*** 建立websocket连接* @param {Function} onConnecting 开始连接时的回调* @param {Function} onConnected 连接成功回调* @param {Function} onError 连接异常或断开回调*/
function connect(onConnecting, onConnected, onError) {onConnecting instanceof Function && onConnecting();var sock = new SockJS(endpoint);client = Stomp.over(sock);console.log("ws: start connect to " + endpoint);client.connect({}, function (frame) {errorTimes = 0;console.log('connected: ' + frame);// 连接成功后重新订阅subscribes.forEach(function (item) {client.subscribe(item.destination, function (resp) {console.debug("ws收到消息: ", resp);item.cb(JSON.parse(resp.body));});});onConnected instanceof Function && onConnected();}, function (err) {errorTimes = errorTimes > 8 ? 0 : errorTimes;var nextTime = ++errorTimes * 3000;console.warn("与服务器断开连接," + nextTime + " 秒后重新连接", err);setTimeout(function () {console.log("尝试重连……");connect(onConnecting, onConnected, onError);}, nextTime);onError instanceof Function && onError();});
}/*** 订阅消息,若当前未连接,则会在连接成功后自动订阅** 注意,为防止重连导致重复订阅,请勿使用匿名函数做回调** @param {String} destination 目标* @param {Function} cb 回调*/
function subscribe(destination, cb) {var exist = subscribes.filter(function (sub) {return sub.destination === destination && sub.cb === cb});// 防止重复订阅if (exist && exist.length) {return;}// 记录所有订阅,在连接成功时统一处理subscribes.push({destination: destination,cb: cb});if (client && client.connected) {client.subscribe(destination, function (resp) {console.debug("ws收到消息: ", resp);cb instanceof Function && cb(JSON.parse(resp.body));});} else {console.warn("ws未连接,暂时无法订阅:" + destination)}
}/*** 发送消息* @param {String} destination 目标* @param {Object} msg 消息体对象*/
function send(destination, msg) {if (!client) {console.error("客户端未连接,无法发送消息!")}client.send(destination, {}, JSON.stringify(msg));
}window.onbeforeunload = function () {// 当窗口关闭时断开连接if (client && client.connected) {client.disconnect(function () {console.log("websocket disconnected ");});}
};

前端的html页面index.html如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STOMP</title>
</head>
<body>
<h1 id="tip">Welcome!</h1>
<p>状态: <span id="status"></span></p>
<input type="text" id="content" placeholder="请输入要发送的消息"> <br>
<button onclick="sendTextMsg()">发送</button>
<ul id="ul">
</ul>
<script th:src="@{lib/sockjs.min.js}"></script>
<script th:src="@{lib/stomp.min.js}"></script>
<script th:src="@{stomp-client.js}"></script>
<script>connect(function () {statusChange("连接中...");}, function () {statusChange("在线");// 注意,为防止重连导致重复订阅,请勿使用匿名函数做回调subscribe("/user/topic/subNewMsg", onNewMsg);}, function () {statusChange("离线");});function onNewMsg(msg) {var li = document.createElement("li");li.innerText = msg.content;document.getElementById("ul").appendChild(li);}function sendTextMsg() {var content = document.getElementById("content").value;var msg = {msgType: 1,content: content};send("/app/echo", msg);}function statusChange(status) {document.getElementById("status").innerText = status;}
</script>
</body>
</html>

后端代码

依赖引入,主要引入下面的包,其它的包略过

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

配置类

@Slf4j
@Setter
@Configuration
@EnableWebSocketMessageBroker
@ConfigurationProperties(prefix = "websocket")
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer, ApplicationListener<BrokerAvailabilityEvent> {private final BrokerConfig brokerConfig;private String[] allowOrigins;@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {// 继承DefaultHandshakeHandler并重写determineUser方法,可以自定义如何确定用户// 添加方法:registry.addEndpoint("/ws").setHandshakeHandler(handshakeHandler)registry.addEndpoint("/ws").setAllowedOrigins(allowOrigins).withSockJS();}/*** 配置消息代理*/@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.setApplicationDestinationPrefixes("/app");if (brokerConfig.isUseSimpleBroker()) {// 使用 SimpleBroker// 配置前缀, 有这些前缀的消息会路由到brokerregistry.enableSimpleBroker("/topic", "/queue")//配置stomp协议里, server返回的心跳.setHeartbeatValue(new long[]{10000L, 10000L})//配置发送心跳的scheduler.setTaskScheduler(new DefaultManagedTaskScheduler());} else {// 使用外部 Broker// 指定前缀,有这些前缀的消息会路由到brokerregistry.enableStompBrokerRelay("/topic", "/queue")// 广播用户目标,如果要推送的用户不在本地,则通过 broker 广播给集群的其他成员.setUserDestinationBroadcast("/topic/log-unresolved-user")// 用户注册广播,一旦有用户登录,则广播给集群中的其他成员.setUserRegistryBroadcast("/topic/log-user-registry")// 虚拟地址.setVirtualHost(brokerConfig.getVirtualHost())// 用户密码.setSystemLogin(brokerConfig.getUsername()).setSystemPasscode(brokerConfig.getPassword()).setClientLogin(brokerConfig.getUsername()).setClientPasscode(brokerConfig.getPassword())// 心跳间隔.setSystemHeartbeatSendInterval(10000).setSystemHeartbeatReceiveInterval(10000)// 使用 setTcpClient 以配置多个 broker 地址,setRelayHost/Port 只能配置一个.setTcpClient(createTcpClient());}}/*** 创建 TcpClient 工厂,用于配置多个 broker 地址*/private ReactorNettyTcpClient<byte[]> createTcpClient() {return new ReactorNettyTcpClient<>(// BrokerAddressSupplier 用于获取中继地址,一次只使用一个,如果该中继出错,则会获取下一个client -> client.addressSupplier(brokerConfig.getBrokerAddressSupplier()),new StompReactorNettyCodec());}@Overridepublic void onApplicationEvent(BrokerAvailabilityEvent event) {if (!event.isBrokerAvailable()) {log.warn("stomp broker is not available!!!!!!!!");} else {log.info("stomp broker is available");}}
}

消息处理

@Slf4j
@Controller
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class StompController {private final SimpMessageSendingOperations msgOperations;private final SimpUserRegistry simpUserRegistry;/*** 回音消息,将用户发来的消息内容加上 Echo 前缀后推送回客户端*/@MessageMapping("/echo")public void echo(Principal principal, Msg msg) {String username = principal.getName();msg.setContent("Echo: " + msg.getContent());msgOperations.convertAndSendToUser(username, "/topic/subNewMsg", msg);int userCount = simpUserRegistry.getUserCount();int sessionCount = simpUserRegistry.getUser(username).getSessions().size();log.info("当前本系统总在线人数: {}, 当前用户: {}, 该用户的客户端连接数: {}", userCount, username, sessionCount);}
}

实现效果

在这里插入图片描述

报文分析

开启调试模式,我们根据报文来分析一下前后端互通的报文
在这里插入图片描述

握手

客户端请求报文如下

GET ws://localhost:8025/ws/035/5hy4avgm/websocket HTTP/1.1
Host: localhost:8025
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/537.36
Upgrade: websocket
Origin: http://localhost:8025
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: 略
Sec-WebSocket-Key: PlMHmdl2JRzDAVk3feOaeA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

服务端响应握手请求

HTTP/1.1 101
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: 9CKY8n1j/cHoKsWmpmX4pNlQuZg=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Date: Thu, 08 Feb 2024 06:58:28 GMT

stomp报文分析

在浏览器消息一栏,我们可以看到长连接过程中通信的报文
在这里插入图片描述
下面来简单分析一下stomp的报文
在这里插入图片描述

客户端请求连接

其中\n表示换行

["CONNECT\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000"
]

可以看到请求连接的命令是CONNECT,连接报文里面还包含了心跳的信息

服务端返回连接成功

["CONNECTED\nversion:1.1\nheart-beat:10000,10000\nuser-name:admin\n\n\u0000"
]

CONNECTED是服务端连接成功的命令,报文中也包含了心跳的信息

客户端订阅

订阅的目的地是:/user/topic/subNewMsg

["SUBSCRIBE\nid:sub-0\ndestination:/user/topic/subNewMsg\n\n\u0000"]

客户端发送消息

发送的目的地是:/app/echo

["SEND\ndestination:/app/echo\ncontent-length:35\n\n{\"msgType\":1,\"content\":\"你好啊\"}\u0000"
]

服务端响应消息

响应的目的地是:/user/topic/subNewMsg,当订阅了这个目的地的,方法,将会被回调

["MESSAGE\ndestination:/user/topic/subNewMsg\ncontent-type:application/json;charset=UTF-8\nsubscription:sub-0\nmessage-id:5hy4avgm-1\ncontent-length:41\n\n{\"content\":\"Echo: 你好啊\",\"msgType\":1}\u0000"
]

心跳报文

可以看到,约每隔10S,客户端和服务端都有一次心跳报文,发送的报文内容为一个回车。

[\n]

在这里插入图片描述

项目链接:https://gitee.com/syk1234/stomp-demo.git

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习10-特征缩放

特征缩放的目的是确保不同特征的数值范围相近&#xff0c;使得模型在训练过程中更加稳定&#xff0c;加速模型收敛&#xff0c;提高模型性能。具体而言&#xff0c;零均值和单位方差的目标有以下几点好处&#xff1a; 1. 均值为零&#xff08;Zero Mean&#xff09;&#xff1a…

15 ABC基于状态机的按键消抖原理与状态转移图

1. 基于状态机的按键消抖 1.1 什么是按键&#xff1f; 从按键结构图10-1可知&#xff0c;按键按下时&#xff0c;接点&#xff08;端子&#xff09;与导线接通&#xff0c;松开时&#xff0c;由于弹簧的反作用力&#xff0c;接点&#xff08;端子&#xff09;与导线断开。 从…

【开源】SpringBoot框架开发天沐瑜伽馆管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 瑜伽课程模块2.3 课程预约模块2.4 系统公告模块2.5 课程评价模块2.6 瑜伽器械模块 三、系统设计3.1 实体类设计3.1.1 瑜伽课程3.1.2 瑜伽课程预约3.1.3 系统公告3.1.4 瑜伽课程评价 3.2 数据库设计3.2.…

牛客周赛 Round 32 F.小红的矩阵修改【三进制状态压缩dp】

原题链接&#xff1a;https://ac.nowcoder.com/acm/contest/75174/F 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K 64bit IO Format: %lld 题目描述 小红拿到了一个字符矩阵&#xff0c;矩阵中仅包含&q…

java 执行方式和类加载过程

java默认属于混合执行&#xff1a; 编译和解释并存 java先进行解释执行&#xff0c;遇到多次重复的代码会把它编程成可执行文件&#xff0c;方便下次直接执行。 可以通过VM参数来修改执行方式。 类加载过程

红队笔记Day2 -->上线不出网机器

今天就来讲一下在企业攻防中如何上线不出网的机器&#xff01;&#xff01; 1.基本网络拓扑 基本的网络拓扑就是这样 以下是对应得的P信息&#xff0c;其中的52网段充当一个内网的网段&#xff0c;而111充当公网网段 先ping一下&#xff0c;确保外网ping不通内网&#xff0c;内…

微信小程序(四十一)wechat-http的使用

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.模块下载 2.模块的使用 在终端输入npm install wechat-http 没有安装成功vue的先看之前的一篇 微信小程序&#xff08;二十&#xff09;Vant组件库的配置- 如果按以上的成功配置出现如下报错先输入以下语句 …

DS:单链表实现队列

创作不易&#xff0c;友友们来个三连支持吧&#xff01; 一、队列的概念 队列&#xff1a;是只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out)的特点。 入队列&#xff1a;进行插入操作…

leetcode题目记录

文章目录 单调栈[127. 单词接龙](https://leetcode.cn/problems/word-ladder/)[139. 单词拆分](https://leetcode.cn/problems/word-break/)[15. 三数之和](https://leetcode.cn/problems/3sum/)[140. 单词拆分 II](https://leetcode.cn/problems/word-break-ii/)[113. 路径总和…

《数字孪生城市建设指引报告(2023年)》指引智慧城市行动方向

2023年12月27日&#xff0c;中国信息通信研究院&#xff08;简称“中国信通院”&#xff09;产业与规划研究所、中国互联网协会数字孪生技术应用工作委员会和苏州工业园区数字孪生创新坊联合发布《数字孪生城市建设指引报告&#xff08;2023年&#xff09;》。该报告提出了三大…

Linux:docker在线仓库(docker hub 阿里云)基础操作

把镜像放到公网仓库&#xff0c;这样可以方便大家一起使用&#xff0c;当需要时直接在网上拉取镜像&#xff0c;并且你可以随时管理自己的镜像——删除添加或者修改。 1.docker hub仓库 2.阿里云加速 3.阿里云仓库 由于docker hub是国外的网站&#xff0c;国内的对数据的把控…

Verilog刷题笔记30

题目&#xff1a; You are provided with a BCD one-digit adder named bcd_fadd that adds two BCD digits and carry-in, and produces a sum and carry-out. 解题&#xff1a; module top_module( input [399:0] a, b,input cin,output cout,output [399:0] sum );reg [99…

代码随想录day20--二叉树的应用8

LeetCode669.修剪二叉搜索树 题目描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没…

【ES】--Elasticsearch的分词器深度研究

目录 一、问题描述及分析二、analyze分析器原理三、 multi-fields字段支持多场景搜索(如同时简繁体、拼音等)1、ts_match_analyzer配置分词2、ts_match_all_analyzer配置分词3、ts_match_1_analyzer配置分词4、ts_match_2_analyzer配置分词5、ts_match_3_analyzer配置分词6、ts…

ctfshow-文件上传(web151-web161)

目录 web151 web152 web153 web154 web155 web156 web157 web158 web159 web160 web161 web151 提示前台验证不可靠 那限制条件估计就是在前端设置的 上传php小马后 弹出了窗口说不支持的格式 查看源码 这一条很关键 这种不懂直接ai搜 意思就是限制了上传类型 允许…

Ubuntu Desktop - Files Preferences

Ubuntu Desktop - Files Preferences 1. Behavior2. ViewsReferences 1. Behavior Go to file browser’s Menu -> Edit -> Preferences -> Behavior 2. Views Go to file browser’s Menu -> Edit -> Preferences -> Views ​​​ References [1] Yong…

EMC学习笔记(二十五)降低EMI的PCB设计指南(五)

线缆和连接器 1 差模和共模噪声2 串扰3 返回路径数量4 外部PCB -IO 布局建议5 防止噪音和静电放电 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 设计良好的两层板&#xff0c;和大多数四层板&#xff0c;有最小的辐射。系统级的问题是由于将PCB与任何板外支持…

vector容器

1. vector基本概念 1.1 功能&#xff1a; vector数据结构和数组非常相似&#xff0c;也称为单端数组 vector与普通数组区别&#xff1a; 不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a; 并不是在原空间之后续接新空间&#xff0c;而是找更…

视频讲解:优化柱状图

你好&#xff0c;我是郭震 AI数据可视化 第三集&#xff1a;美化柱状图&#xff0c;完整视频如下所示&#xff1a; 美化后效果前后对比&#xff0c;前&#xff1a; 后&#xff1a; 附完整案例源码&#xff1a; util.py文件 import platformdef get_os():os_name platform.syst…

方舟基金:若美机构按最大夏普率配置比特币,则有望将其推升至230-250万美元...

号外&#xff1a;教链内参2.12《方舟基金重磅报告〈大胆想象2024〉全文pdf》 方舟基金&#xff08;Ark Invest&#xff09;的木头姐&#xff08;Cathie Wood&#xff09;是业内的老熟人了。她一向以大胆的预测而著称。比如就在2023年10月份&#xff0c;木头姐在采访中就曾直言&…