特别说明:1. 本文基于Springboot spring-boot-starter-parent 1.5.1.RELEASE编码,在不同的版本中部分方法有区别。2. 因为博客字数限制,拆分成了两篇文章
第一篇地址:Spring Springboot实现websocket通讯-1
第二篇地址:Spring Springboot实现websocket通讯-2
前面两种建立websocket通讯,不管是用javax的包还是spring的包都是用的比较底层的协议,下面我们来看看用上层的STOMP来建立websocket通讯
SockJs+Spring-WebSocket时,由于SockJs与Spring WebSocket之间采用JSON通讯,需要引入jackson 2的相关jar包
com.fasterxml.jackson.core jackson-core 2.6.3com.fasterxml.jackson.core jackson-databind 2.6.3com.fasterxml.jackson.core jackson-annotations 2.6.3
前面已经提到了STOMP是一个上层协议,STOMP 在 WebSocket 之上提供了一个基于 帧的线路格式层,用来定义消息语义。STOMP 帧:该帧由命令,一个或多个 头信息 以及 负载所组成。如下就是发送 数据的一个 STOMP帧:
SENDdestination:/app/marcocontent-length:20 {"message":"hello word!"}
SEND:STOMP命令,表明会发送一些内容;destination:头信息,用来表示消息发送到哪里;content-length:头信息,用来表示 负载内容的 大小;空行:帧内容(负载)内容
要使用STOMP 通讯,服务端,和客户端都必须支持,服务端的准备步骤
服务端准备工作
- 我们已经配置了STOMP通讯的配置类 WebSocketStompConfig
- 配置了WebSocketChannelInterceptor 和 WebSocketHandshakeInterceptor 两个自定义拦截器
- 一个WebSocketStompController 用于接收客户端消息和响应客户端
- 一个简单的MVC controller 用于跳转websocket 页面
在Spring中启用STOMP通讯不用我们自己去写原生态的帧,spring的消息功能是基于代理模式构建,其实说得复杂,都是封装好了的,如果需要开启SOMP,只需要在websocket配置类上使用@EnableWebSocketMessageBroker (注解的作用为能够在 WebSocket 上启用 STOMP),并实现WebSocketMessageBrokerConfigurer接口,有些教程在这一步会继承AbstractWebSocketMessageBrokerConfigurer 类,我们看一下AbstractWebSocketMessageBrokerConfigurer类的源码,可以看到都是空方法,也是实现的接口,这里推荐自己实现接口,因为官方API上AbstractWebSocketMessageBrokerConfigurer已经标记为废弃
AbstractWebSocketMessageBrokerConfigurer 抽象类
public abstract class AbstractWebSocketMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer { public AbstractWebSocketMessageBrokerConfigurer() { } public void configureWebSocketTransport(WebSocketTransportRegistration registration) { } public void configureClientInboundChannel(ChannelRegistration registration) { } public void configureClientOutboundChannel(ChannelRegistration registration) { } public boolean configureMessageConverters(List messageConverters) { return true; } public void addArgumentResolvers(List argumentResolvers) { } public void addReturnValueHandlers(List returnValueHandlers) { } public void configureMessageBroker(MessageBrokerRegistry registry) { }}
WebSocketMessageBrokerConfigurer接口
public interface WebSocketMessageBrokerConfigurer { // 添加这个Endpoint,这样在网页中就可以通过websocket连接上服务,也就是我们配置websocket的服务地址,并且可以指定是否使用socketjs void registerStompEndpoints(StompEndpointRegistry var1); // 配置发送与接收的消息参数,可以指定消息字节大小,缓存大小,发送超时时间 void configureWebSocketTransport(WebSocketTransportRegistration var1); // 设置输入消息通道的线程数,默认线程为1,可以自己自定义线程数,最大线程数,线程存活时间 void configureClientInboundChannel(ChannelRegistration var1); // 设置输出消息通道的线程数,默认线程为1,可以自己自定义线程数,最大线程数,线程存活时间 void configureClientOutboundChannel(ChannelRegistration var1); // 添加自定义的消息转换器,spring 提供多种默认的消息转换器,返回false,不会添加消息转换器,返回true,会添加默认的消息转换器,当然也可以把自己写的消息转换器添加到转换链中 boolean configureMessageConverters(List var1); // 配置消息代理,哪种路径的消息会进行代理处理 void configureMessageBroker(MessageBrokerRegistry var1); // 自定义控制器方法的参数类型,有兴趣可以百度google HandlerMethodArgumentResolver这个的用法 void addArgumentResolvers(List var1); // 自定义控制器方法返回值类型,有兴趣可以百度google HandlerMethodReturnValueHandler这个的用法 void addReturnValueHandlers(List var1);}
在registerStompEndpoints 方法中,我们可以设置websocket服务的地址,同样,我们也可以根据自身业务需求,去添加拦截器,例如前文我们写的WebSocketHandshakeInterceptor拦截器,可以获取到httpsession,同样,当我们把信息存入map 后,都可以通过通过WebSocketSession的getAttributes()下提供get方法获取
/** * 添加这个Endpoint,这样在网页中就可以通过websocket连接上服务, * 也就是我们配置websocket的服务地址,并且可以指定是否使用socketjs * * @param registry */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { /* * 1. 将 /serviceName/stomp/websocketJs路径注册为STOMP的端点, * 用户连接了这个端点后就可以进行websocket通讯,支持socketJs * 2. setAllowedOrigins("*")表示可以跨域 * 3. withSockJS()表示支持socktJS访问 * 4. 添加自定义拦截器,这个拦截器是上一个demo自己定义的获取httpsession的拦截器 */ registry.addEndpoint("/stomp/websocketJS") .setAllowedOrigins("*") .withSockJS() .setInterceptors(new WebSocketHandshakeInterceptor()) ; /* * 看了下源码,它的实现类是WebMvcStompEndpointRegistry , * addEndpoint是添加到WebMvcStompWebSocketEndpointRegistration的集合中, * 所以可以添加多个端点 */ registry.addEndpoint("/stomp/websocket"); }
如果我们业务关心,用户的数量,在线数量,连接状况等数据,我们也可以通过ChannelRegistration对象的setInterceptors方法添加监听,这里先展示一个完整的实现类,监听接口在后面会介绍,代码中的WebSocketHandshakeInterceptor 拦截器,是上一个例子已经实现的,用于存储httpsession,WebSocketChannelInterceptor 拦截器 ,在这个拦截器中可以做一些在线人数统计等操作,后面会介绍
package com.wzh.demo.websocket.config;import com.wzh.demo.websocket.handler.MyPrincipalHandshakeHandler;import com.wzh.demo.websocket.interceptor.WebSocketChannelInterceptor;import com.wzh.demo.websocket.interceptor.WebSocketHandshakeInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.messaging.converter.MessageConverter;import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;import org.