spring boot2 (32)-WebSocket和stomp消息

本文介绍如何使用Spring Boot实现WebSocket的Broker服务,并通过STOMP协议实现客户端与服务器间的消息推送及点对点通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WebSocket可实现浏览器和服务器之间的通信,如在线聊天,消息推送等,其基于tcp协议来传输数据。而stomp是一种更高级的协议,可以更加方便的实现WebSocket。

broker和客户端

客户端可以是任何语言,如js,php等,只须使用stomp协议来收发消息,broker可对消息进行处理或转发等。本篇将介绍以spring boot实现broker,以js实现客户端。

pom.xml

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

配置消息端点

spring boot会自动配置好broker,我们只需添加端点如/endpoint1,客户端通过端点建立连接。

@SpringBootConfiguration
@EnableWebSocketMessageBroker	//自动配置Broker
public class WebSocketConfig  implements WebSocketMessageBrokerConfigurer  {

    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpoint1").withSockJS();
    }

消息广播

java代码:每当请求/sent时向/topic1发送消息hello,world,所有订阅了/topic1的客户端都会收到这个消息。

    @Autowired
    private SimpMessagingTemplate messagingTemplate;
    
    @GetMapping("/sent")
    public void sent(HttpSession session) {
    	messagingTemplate.convertAndSend("/topic1", "hello,world"); 
    }
js代码:向/endpoint1端点建立连接,同时订阅/topic1的消息,并弹出窗口,显示消息。中间有个{}传参后面再讲。
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script type="text/javascript">
var socket = new SockJS('/endpoint1'); 
var stompClient = Stomp.over(socket);
stompClient.connect({}, function() {
    stompClient.subscribe('/topic1', function(respnose){
    	alert(respnose.body);
    });
});
启动项目,首先打开js的页面即会建立连接,每当服务器收到/sent请求便会向topic1发消息,页面会自己弹出hello,world。

消息接收 

java代码:当客户端向/hello发消息时,在服务器控制台打印消息

    @MessageMapping("/hello")
    public void hello(String message) {
    	    System.out.println(message);
html代码:一个按钮,用来触发发消息事件
<button onclick="sendTest();">发消息</button>
js代码:加到前面js代码中,向/hello发消息,"test"会传给后台的message。这里也有个{}和connect中的{}是一样的作用,用来传递头部参数,后面会讲。
function sendTest() {
    stompClient.send("/hello", {}, "test");
}

权限拦截器

修改端点配置代码,设置拦截器。每当客户端连接端点时都会被拦截,在这里可以读取到session,进行验证。和mvc拦截器类似,参考第19篇。当连接建立以后,通过该连接收发消息就不需要再次验证了。

    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpoint1").withSockJS()
        .setInterceptors(new HandshakeInterceptor(){

			@Override
			public void afterHandshake(ServerHttpRequest arg0, ServerHttpResponse arg1, WebSocketHandler arg2,
					Exception arg3) {
			}

			@Override
			public boolean beforeHandshake(ServerHttpRequest arg0, ServerHttpResponse arg1, WebSocketHandler arg2,
					Map<String, Object> arg3) throws Exception {
				//拿到session,自己完成验证代码
				HttpSession session = getSession(arg0);
				return true;
			}
			
			private HttpSession getSession(ServerHttpRequest request) {
				if (request instanceof ServletServerHttpRequest) {
					ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
					return serverRequest.getServletRequest().getSession();
				}
				return null;
			}
        }); 请求
    }

请求拦截器

上面客户端建立连接时,可以拿到session。而一但连接建立成功,就可以直接收发消息,但是无法拿到session或request。此时可以使用Principal来识别 每条消息的用户来源。

在前面的WebSocketConfig类中加入以下方法,客户端建立连接和收发消息时都会被它拦截,不影响上面的权限拦截器。这段代码意思是:当用户建立连接时,将前面说的{}中的参数username存入Principa。以后每次客户端发消息时,都可以从Principa读取到其username

   @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
    	//注册拦截器
        registration.interceptors(new ChannelInterceptorAdapter() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                //判断客户端发出的命令是不是CONNECT
                if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                	//这里的username参数就是从前面的{}中传过来的
                    Principal user =  new MyPrincipal(accessor.getNativeHeader("username").get(0));
                    //类似于设置session,在控制层获取该参数时,会调用下面的MyPrincipal.getName(),下面会讲
                    accessor.setUser(user);
                }
                //拦截处理完后转发message,如果不允许该消息,可以返回null
                return message;
            }
        });
    }
    //内部类实现Principal接口
	class MyPrincipal implements Principal{
		private String name;
		public MyPrincipal(String name){
			this.name=name;
		}
		@Override
		public String getName() {
			return name;
		}
	}
修改/hello接收消息方法的参数,以下的principal.getName()就是上面的MyPrincipal.getName(),可获取username参数值
    @MessageMapping("/hello")
	public void hello(Principal principal,String message) {
    	System.out.println(principal.getName());
修改js中的{}传参如下,对应拦截器中的accessor.getNativeHeader("username")
stompClient.connect({username:"tom"}, function() {
此时,客户端连接后,每次点击按钮向/hello发消息时,都会打印出其username,以此确认消息来源用户。

点对点消息

点对点即向特定用户发消息,上面已经确认消息来源,现在只需要确认特定的消息接收者。
修改hello方法,将tom发过来的message单独发给jerry,而不再是公共广播
    @MessageMapping("/hello")
	public void hello(Principal principal,String message) {
    	messagingTemplate.convertAndSendToUser("jerry","/topic1", principal.getName()+"say:"+message); 
修改js,注意固定语法/user/jerry,指只订阅user用户jerry的消息
	stompClient.subscribe('/user/jerry/topic1', function(respnose){
    	alert(respnose.body);
    });
此时,点击消息按钮时,将弹出来自tom的消息。这里发消息和收消息都在一个页面,出于方便我把username写死了,实际可以动态设置用户名,不同用户互发消息,不解释了。
<think>好的,用户想了解如何使用Spring Bootspring-boot-starter-websocket进行WebSocket开发,需要教程、示例依赖配置。首先,我需要回忆相关的知识点,结合提供的引用内容来组织回答。 首先,查看用户提供的引用资料,引用34提到了依赖配置基本示例,引用1、2、5可能提供更多细节或不同的方法。用户的问题集中在使用spring-boot-starter-websocket,所以需要重点参考官方集成方式,可能引用34比较关键。 接下来,整理步骤。通常,Spring Boot集成WebSocket的步骤包括添加依赖、配置WebSocket、创建处理器、注册端点、前端连接等。根据引用3,依赖是spring-boot-starter-websocket,配置需要启用WebSocket支持,可能使用@EnableWebSocketMessageBroker或者更基础的配置。引用4中的主类示例是标准的Spring Boot启动类,可能需要在配置类中进行更多设置。 然后,处理器的创建,如引用4中的示例代码可能包含处理消息的类,使用@MessageMapping注解。需要说明如何定义消息处理方法,可能使用STOMP或SockJS,但用户可能只需要基础示例,所以保持简单。 前端连接部分,需要提供JavaScript代码示例,使用WebSocket或SockJS库连接到后端端点。引用3提到sockjswebstomp-client,但用户可能希望基础示例,所以使用原生WebSocket或简单的SockJS连接。 此外,可能涉及跨域配置,但用户的问题中没有提到,暂时可以省略,或者在注意事项中提到。性能方面,引用5提到Netty的高性能方案,可以作为扩展或优化建议。 需要确保依赖配置正确,引用3中给出了Maven依赖,需要检查是否正确。同时,代码示例要符合Spring Boot的最新实践,比如使用@Configuration类进行配置,启用WebSocket,并注册端点。 可能的问题点:用户是否使用STOMP?根据引用34,示例可能没有使用STOMP,而是更底层的WebSocket处理。但通常SpringWebSocket支持推荐使用STOMP子协议,所以需要明确说明。 现在,组织回答的结构:依赖配置->启用配置->创建处理器->注册端点->前端连接->测试步骤。每个部分结合引用内容,比如引用3的依赖,引用4的主类,引用34的处理方法。 最后,生成相关问题,可能涉及性能优化、安全性、集群处理等,参考提供的引用中的其他内容,如引用5的Netty方案可作为优化相关问题。</think>### Spring Boot WebSocket开发教程 #### 一、依赖配置 在`pom.xml`中添加核心依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 该依赖包含WebSocket协议支持与Spring的集成组件[^3]。 #### 二、基础配置类 创建配置类启用WebSocket支持: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/ws") .setAllowedOrigins("*"); } @Bean public WebSocketHandler myHandler() { return new TextSocketHandler(); } } ``` 此配置注册了WebSocket端点`/ws`并指定消息处理器[^4]。 #### 三、消息处理器实现 创建文本消息处理器: ```java public class TextSocketHandler extends TextWebSocketHandler { @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String payload = message.getPayload(); session.sendMessage(new TextMessage("ECHO: " + payload)); } } ``` 该处理器实现消息的接收与回传功能。 #### 四、Spring Boot主类 标准启动类配置: ```java @SpringBootApplication public class WebSocketApplication { public static void main(String[] args) { SpringApplication.run(WebSocketApplication.class, args); } } ``` 确保应用具有自动配置能力。 #### 五、前端连接示例 使用JavaScript建立连接: ```javascript const socket = new WebSocket('ws://localhost:8080/ws'); socket.onopen = () => { socket.send('Hello Server'); }; socket.onmessage = (event) => { console.log('Received: ' + event.data); }; ``` #### 六、测试流程 1. 启动Spring Boot应用 2. 打开包含上述JS代码的HTML页面 3. 检查浏览器控制台输出是否收到`ECHO: Hello Server` #### 注意事项 1. 生产环境需配置`setAllowedOrigins`限制跨域访问 2. 可通过`@Autowired`注入Spring管理的Bean到处理器中 3. 高并发场景建议使用异步消息处理[^5]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值