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

相关文章

Spring boot整合dubbo+zookeeper的详细过程

《Springboot整合dubbo+zookeeper的详细过程》本文讲解SpringBoot整合Dubbo与Zookeeper实现API、Provider、Consumer模式,包含依赖配置、... 目录Spring boot整合dubbo+zookeeper1.创建父工程2.父工程引入依赖3.创建ap

C#连接SQL server数据库命令的基本步骤

《C#连接SQLserver数据库命令的基本步骤》文章讲解了连接SQLServer数据库的步骤,包括引入命名空间、构建连接字符串、使用SqlConnection和SqlCommand执行SQL操作,... 目录建议配合使用:如何下载和安装SQL server数据库-CSDN博客1. 引入必要的命名空间2.

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu