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

相关文章

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Java 8 Stream filter流式过滤器详解

《Java8Streamfilter流式过滤器详解》本文介绍了Java8的StreamAPI中的filter方法,展示了如何使用lambda表达式根据条件过滤流式数据,通过实际代码示例,展示了f... 目录引言 一.Java 8 Stream 的过滤器(filter)二.Java 8 的 filter、fi

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入