SpringBoot中建立WebSocket连接(STOMP实现发送消息给指定用户)

本文主要是介绍SpringBoot中建立WebSocket连接(STOMP实现发送消息给指定用户),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文来自:https://blog.csdn.net/qq_28988969/article/details/78134114?locationNum=9&fps=1

十分感谢博主解决了我的人生大事啊!

使用STOMP实现发送消息给指定用户步骤如下:

  • 添加pom文件依赖
  • 书写客户端用户实体类
  • 书写客户端渠道拦截适配器
  • 配置websocket stomp
  • 书写控制层
  • 书写客户端

1.添加pom文件依赖

<!-- springboot websocket -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

2.书写客户端用户实体类

自定义客户端用户实体类,封装来自于客户端的信息,相当于为每一个客户端提供唯一的标识

package com.ahut.entity;import java.security.Principal;/*** * @ClassName: User* @Description: 客户端用户* @author cheng* @date 2017年9月29日 下午3:02:54*/
public final class User implements Principal {private final String name;public User(String name) {this.name = name;}@Overridepublic String getName() {return name;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3.书写客户端渠道拦截适配器

利用拦截的方式,获取包含在stomp中的用户信息,并将认证的用户信息设置到当前的访问器中

package com.ahut.websocket;import java.util.LinkedList;
import java.util.Map;import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.MessageHeaderAccessor;import com.ahut.entity.User;/*** * @ClassName: UserInterceptor* @Description: 客户端渠道拦截适配器* @author cheng* @date 2017年9月29日 下午2:40:12*/
public class UserInterceptor extends ChannelInterceptorAdapter {/*** 获取包含在stomp中的用户信息*/@SuppressWarnings("rawtypes")@Overridepublic Message<?> preSend(Message<?> message, MessageChannel channel) {StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);if (StompCommand.CONNECT.equals(accessor.getCommand())) {Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);if (raw instanceof Map) {Object name = ((Map) raw).get("name");if (name instanceof LinkedList) {// 设置当前访问器的认证用户accessor.setUser(new User(((LinkedList) name).get(0).toString()));}}}return message;}
}
  • 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
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

4.配置websocket stomp

package com.ahut.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;import com.ahut.websocket.UserInterceptor;/*** * @ClassName: WebSocketStompConfig* @Description: springboot websocket stomp配置* @author cheng* @date 2017年9月27日 下午3:45:36*/@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer {/*** 注册stomp的端点*/@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {// 允许使用socketJs方式访问,访问点为webSocketServer,允许跨域// 在网页上我们就可以通过这个链接// http://localhost:8080/webSocketServer// 来和服务器的WebSocket连接registry.addEndpoint("/webSocketServer").setAllowedOrigins("*").withSockJS();}/*** 配置信息代理*/@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {// 订阅Broker名称registry.enableSimpleBroker("/queue", "/topic");// 全局使用的消息前缀(客户端订阅路径上会体现出来)registry.setApplicationDestinationPrefixes("/app");// 点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/// registry.setUserDestinationPrefix("/user/");}/*** 配置客户端入站通道拦截器*/@Overridepublic void configureClientInboundChannel(ChannelRegistration registration) {registration.setInterceptors(createUserInterceptor());}/*** * @Title: createUserInterceptor* @Description: 将客户端渠道拦截器加入spring ioc容器* @return*/@Beanpublic UserInterceptor createUserInterceptor() {return new UserInterceptor();}}
  • 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
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

5.书写控制层

package com.ahut.action;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.user.SimpUser;
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import com.ahut.entity.ServerMessage;/*** * @ClassName: WebSocketAction* @Description: websocket控制层* @author cheng* @date 2017年9月27日 下午4:20:58*/
@Controller
public class WebSocketAction {private Logger logger = LoggerFactory.getLogger(this.getClass());//spring提供的发送消息模板@Autowiredprivate SimpMessagingTemplate messagingTemplate;@Autowiredprivate SimpUserRegistry userRegistry;@RequestMapping(value = "/templateTest")public void templateTest() {logger.info("当前在线人数:" + userRegistry.getUserCount());int i = 1;for (SimpUser user : userRegistry.getUsers()) {logger.info("用户" + i++ + "---" + user);}//发送消息给指定用户messagingTemplate.convertAndSendToUser("test", "/queue/message", new ServerMessage("服务器主动推的数据"));}}
  • 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
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

代码分析: 
SimpUserRegistry用来获取连接的客户端信息 
userRegistry.getUsers()将返回一个用户列表

模拟发送信息给指定用户,浏览器访问

localhost:8080/templateTest
  • 1

使用test作为连接用户名,并且订阅了/user/queue/message主题的客户端就会收到服务器主动推送的消息

查看convertAndSendToUser的源码如下:

    @Overridepublic void convertAndSendToUser(String user, String destination, Object payload, Map<String, Object> headers,MessagePostProcessor postProcessor) throws MessagingException {Assert.notNull(user, "User must not be null");user = StringUtils.replace(user, "/", "%2F");super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以发现messagingTemplate.convertAndSendToUser(“test”, “/queue/message”, new ServerMessage(“服务器主动推的数据”));最终发送的目的地地址为:

/user/test/queue/message
  • 1

若用户名中包含”/”,则替换成”%2F”

6.书写客户端

<!DOCTYPE html>
<html><head><title>stomp</title>
</head><body>Welcome<br/><input id="text" type="text" /><button onclick="send()">发送消息</button><button onclick="subscribe3()">订阅消息/user/queue/message</button><hr/><div id="message"></div>
</body><script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script type="text/javascript">// 建立连接对象(还未发起连接)var socket = new SockJS("http://localhost:8080/webSocketServer");// 获取 STOMP 子协议的客户端对象var stompClient = Stomp.over(socket);// 向服务器发起websocket连接并发送CONNECT帧stompClient.connect({name: 'test' // 携带客户端信息},function connectCallback(frame) {// 连接成功时(服务器响应 CONNECTED 帧)的回调方法setMessageInnerHTML("连接成功");},function errorCallBack(error) {// 连接失败时(服务器响应 ERROR 帧)的回调方法setMessageInnerHTML("连接失败");});//订阅消息function subscribe3() {stompClient.subscribe('/user/queue/message', function (response) {var returnData = JSON.parse(response.body);setMessageInnerHTML("/user/queue/message 你接收到的消息为:" + returnData.responseMessage);});}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}</script></html>
  • 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
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

代码分析: 
当你的客户端连接时,他们必须提供他们的用户名:

// 向服务器发起websocket连接并发送CONNECT帧
stompClient.connect({name: 'test' // 携带客户端信息},function connectCallback(frame) {// 连接成功时(服务器响应 CONNECTED 帧)的回调方法setMessageInnerHTML("连接成功");},function errorCallBack(error) {// 连接失败时(服务器响应 ERROR 帧)的回调方法setMessageInnerHTML("连接失败");}
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

用户需要先订阅/user/queue/message主题,才能收到发送给自己的消息

总结:

客户端订阅:/user/queue/message 
服务器推送指定用户:/user/客户端用户名/queue/message


这篇关于SpringBoot中建立WebSocket连接(STOMP实现发送消息给指定用户)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/605713

相关文章

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

浅析Java中如何优雅地处理null值

《浅析Java中如何优雅地处理null值》这篇文章主要为大家详细介绍了如何结合Lambda表达式和Optional,让Java更优雅地处理null值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录场景 1:不为 null 则执行场景 2:不为 null 则返回,为 null 则返回特定值或抛出异常场景

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

OpenCV图像形态学的实现

《OpenCV图像形态学的实现》本文主要介绍了OpenCV图像形态学的实现,包括腐蚀、膨胀、开运算、闭运算、梯度运算、顶帽运算和黑帽运算,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起... 目录一、图像形态学简介二、腐蚀(Erosion)1. 原理2. OpenCV 实现三、膨胀China编程(

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT