引言 websocket 是 HTML5新增加特性之一,目的是浏览器与服务端建立全双工的通信方式,解决 HTTP请求-响应带来过多的资源消耗,同时对特殊场景应用提供了全新的实现方式,比如聊天、股票交易、游戏等对对实时性要求较高的行业领域。
STOMP STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议。
WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义;
与处在应用层的HTTP不同,WebSocket处在TCP上非常薄的一层,会将字节流转换为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server间的 通信增加适当的消息语义。
如何理解 STOMP 与 WebSocket 的关系:
HTTP协议解决了 web 浏览器发起请求以及 web 服务器响应请求的细节,假设 HTTP 协议 并不存在,只能使用 TCP 套接字来 编写 web 应用,你可能认为这是一件疯狂的事情;
直接使用 WebSocket(SockJS) 就很类似于 使用 TCP 套接字来编写 web 应用,因为没有高层协议,就需要我们定义应用间所发送消息的语义,还需要确保连接的两端都能遵循这些语义;
同 HTTP 在 TCP 套接字上添加请求-响应模型层一样,STOMP 在 WebSocket 之上提供了一个基于帧的线路格式层,用来定义消息语义;
Spring+websocket 添加依赖 需要添加spring-websocket和spring-messaging依赖,注意和spring-core的版本保持一致。
1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-websocket</artifactId > <version > 4.1.9.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-messaging</artifactId > <version > 4.1.9.RELEASE</version > </dependency >
服务端代码 服务端的初始化,只需要两个类:WebsocketConfig (stomp节点配置)和WebSocketController 。
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 import org.springframework.context.annotation.Configuration;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;@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints (StompEndpointRegistry registry) { registry.addEndpoint("/endpointChat" ).withSockJS(); } @Override public void configureMessageBroker (MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue" , "/topic" ); } }
对以上代码分析:
EnableWebSocketMessageBroker 注解表明: 这个配置类不仅配置了 WebSocket,还配置了基于代理的 STOMP 消息;
它复写了 registerStompEndpoints() 方法:添加一个服务端点,来接收客户端的连接。将 “/endpointChat” 路径注册为 STOMP 端点。这个路径与之前发送和接收消息的目的路径有所不同, 这是一个端点,客户端在订阅或发布消息到目的地址前,要连接该端点,即用户发送请求 :URL=’/127.0.0.1:8080/endpointChat’ 与 STOMP server 进行连接,之后再转发到订阅URL;
它复写了 configureMessageBroker() 方法:配置了一个 简单的消息代理,通俗一点讲就是设置消息连接请求的各种规范信息。
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 import org.springframework.messaging.handler.annotation.MessageMapping;import org.springframework.messaging.simp.SimpMessagingTemplate;import org.springframework.stereotype.Controller;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;import com.thinkgem.jeesite.modules.sys.utils.UserUtils;@Controller @RequestMapping("/websocket") public class WebsocketController { @Autowired private SimpMessagingTemplate template; @MessageMapping("/sendMsg") public void roomMessage () { ThreadPoolExecutor executor = new ThreadPoolExecutor(1 , 1 , 0 , TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); executor.execute(new Runnable() { @Override public void run () { template.convertAndSendToUser(userId, "/queue/notifications" ,"新消息:这是websocked测试消息" ); } }); executor.shutdown(); } }
template.convertAndSendToUser(user, dest, message) 这个方法官方给出的解释是 Convert the given Object to serialized form, possibly using a MessageConverter, wrap it as a message and send it to the given destination. 意思就是“将给定的对象进行序列化,使用 ‘MessageConverter’ 进行包装转化成一条消息,发送到指定的目标”,通俗点讲就是我们使用这个方法进行消息的转发发送。
客户端实现 首先引用 sockjs.js 和 stomp.js
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 <script src="/js/common/sockjs.min.js" > <script src ="/js/common/stomp.min.js" > <script type ="text/javascript" > $(function ( ) { connect(); }); function connect ( ) { var sock = new SockJS("http://localhost:8080/endpointChat" ); var stomp = Stomp.over(sock); stomp.connect('guest' , 'guest' , function (frame ) { stomp.subscribe("/user/queue/notifications" , handleNotification); stomp.subscribe('/topic/getResponse' , function (response ) { console .info(response.body); }); stomp.send("URL" , {}, JSON .stringify(message)); function handleNotification (message ) { console .info(message.body); } } </script >
利用 stomp的connect(login, passcode, connectCallback, errorCallback, vhost) 方法建立连接,值得注意的是不同版本的 stomp.js 的 connect() 函数的参数会有所不同;
利用 stomp的subscribe(destination, callback, headers) 方法可以订阅服务器发送来的消息,destination 表示服务器发送消息地址;通过 event 的 body 获取消息内容;
利用 stompClient 的send(destination, headers, body) 方法可以向服务端发送消息,第一个参数为发送消息地址,最后一个参数是发送消息的 json 串;
测试 在客户端请求*/websocket/sendMsg*后会有如下效果:
参考:
Spring Framework Reference Documentation
websocket+spring
spring websocket + stomp 实现广播通信和一对一通信