spring springboot2 结合 websocket+sockJs+stomp 实现个人订阅和广播模式
一 名词解释
1.WebSocket
WebSocket 是发送和接收消息的底层API,WebSocket 协议提供了通过一个套接字实现全双工通信的功能。也能够实现 web 浏览器和 server 间的异步通信,全双工意味着 server 与浏览器间可以发送和接收消息。需要注意的是必须考虑浏览器是否支持
2.SockJs
为了应对许多浏览器不支持WebSocket协议的问题,设计了备选SockJs。
SockJS 是 WebSocket 技术的一种模拟。SockJS 会 尽可能对应 WebSocket API,但如果 WebSocket 技术不可用的话,就会选择另外的通信方式协议。
3.STOMP
SockJS 为 WebSocket 提供了 备选方案。但无论哪种场景,对于实际应用来说,这种通信形式层级过低。下面看一下如何 在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server 间的 通信增加适当的消息语义。(STOMP—— Simple Text Oriented Message Protocol——面向消息的简单文本协议)
4.三者之间的关系
WebSocket 是底层协议,SockJS 是WebSocket 的备选方案,也是 底层协议,而 STOMP 是基于 WebSocket(SockJS) 的上层协议
二 springboot的config
1 2 3 4 5 | <!-- websocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> |
1.基础配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Configuration @EnableWebSocketMessageBroker public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue这两个域上服务端可以向客户端发消息 registry.setApplicationDestinationPrefixes("/app");//客户端向服务器端发送时的主题上面需要加"/app"作为前缀 registry.setUserDestinationPrefix("/user");//指定用户发送一对一的主题,前缀是"/user" } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/stomp/endpoint").withSockJS(); } } |
第一步就是添加注解 此注解 这个配置类不仅配置了 WebSocket,还配置了基于代理的 STOMP 消息
第二步 配置一个消息代理..一个是允许服务器向客户端发送的域 一个是客户端想服务器发送的前缀,最后一个是指定一对第一发送的前缀
第三步 就是配置了节点,并且使用sockjs的方式
2.监听连接与断开
用到了redis管理session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Access (name = "监听websocket连接成功" ) @EventListener public void onConnectEvent(SessionConnectEvent event) { //获取Session连接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //获取SessionId String sessionId = sha.getSessionId(); //存储redis Map<String, String> map = new HashMap<String, String>(); map.put(sessionId, agentNo); redisTemplate.opsForHash().putAll( "webSocket" , map); } @Access (name = "监听websocket断开连接" ) @EventListener public void onDisconnectEvent(SessionDisconnectEvent event) { //获取Session连接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //获取SessionId String sessionId = sha.getSessionId(); //redis删除此sessionId redisTemplate.opsForHash().delete( "webSocket" , sessionId); } } |
三.实现前端代码
1.引入js
1 2 3 4 5 6 7 8 | <!-- websocket ****代表自己的项目路径 这个写自己的 --> <script src= "/***/sockjs-client/sockjs.min.js" ></script> <script src= "/***/stomp-websocket/stomp.min.js" ></script> <!-- 参考 --> <!-- https: //cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js https: //cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js --> |
2.连接和订阅消息
使用了agentNo 区分订阅广播消息与个人消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var stompClient = null ; var socket = null ; function connect() { //从sesionStorage获取坐席工号 下面连接头需要 这个按照自己需求修改 agentNo = sessionStorage.getItem( "agentNo" ); //建立连接对象(还未发起连接) 地址更换成自己的 连接服务器注册端点endPoint时,写访问服务器的全路径URL: socket = new SockJS( '地址' ); //获取 STOMP 子协议的客户端对象 stompClient = Stomp.over(socket); //建立连接,并且连接头上定义了坐席工号 stompClient.connect({ userName: agentNo // <---这是头 在后面的监听 连接成功的类可以获取到 业务代码需要 按需删除 }, function (frame) { console.log( 'Connected: ' + frame); //订阅广播消息 stompClient.subscribe( '/topic/greetings' , function (greeting) { var data = JSON.parse(greeting.body).data; //实现自己的需求 }); //订阅个人消息 stompClient.subscribe( '/user/' + agentNo + '/message' , function (greeting) { var data = JSON.parse(greeting.body); //实现自己的需求 }); }); } |
2.发送消息
1 2 3 4 5 6 7 8 9 | function send(data) { var param = { id: data, release: true } stompClient.send( "/app/release" , {}, JSON.stringify(param)); // <----- app 就是在config中定义的客户端往服务器发送的前缀,param是信息.必须用实体发送后台必须用实体接收 //release是后面controller中定义的 相当于RequestMapping中写的地址一样 } |
四.实现后端代码
1.发送广播
1 2 3 4 5 6 | @Access (name = "发布公告" ) @MessageMapping ( "/release" ) @SendTo ( "/topic/greetings" ) public WebResult release( @RequestBody Announcement announcement) { //执行业务代码逻辑 } |
2.发送个人消息
1 2 3 4 5 6 7 8 | @Autowired private SimpMessagingTemplate messagingTemplate; public void demo() { // messagingTemplate.convertAndSend(url,""); //方法1 //方法2 相当于/user/' + agentNo + '/message' 对应前端订阅的地址 messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message" , "" ); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能