SpringBoot 整合 WebSocket(topic广播)
1、什么是WebSocket
WebSocket为游览器和服务器提供了双工异步通信的功能,即游览器可以向服务器发送消息,服务器也可以向游览器发送消息。WebSocket需游览器的支持,如IE10、Chrome 13+、Firefox 6+,这对我们现在的游览器来说都不是问题。
WebSocket是通过一个socket来实现双工异步通讯能力的。但是直接使用WebSocket(或SockJS:WebSocket协议的模拟,增加了当游览器不支持WebSocket的时候的兼容支持)协议开发程序显得特别繁琐, 我们会使用它的子协议STOMP,它是一个更高级别的协议,STOMP协议使用一个基于帧的格式来定义消息,与HTTP的request和reponse类似(具有类似于@RequestMapping的@MassageMapping)
2、什么是STOMP
STOMP,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。
它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互,类似于OpenWire(一种二进制协议)。由于其设计简单,很容易开发客户端,因此在多种语言和多种平台上得到广泛应用。其中最流行的STOMP消息代理是Apache ActiveMQ。
STOMP协议工作于TCP协议之上,使用了下列命令:
1)、SEND 发送
2)、SUBSCRIBE 订阅
3)、UNSUBSCRIBE 退订
4)、BEGIN 开始
5)、COMMIT 提交
6)、ABORT 取消
7)、ACK 确认
8)、DISCONNECT 断开
3、为什么需要WebSocket
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。如果想持续从服务的获取消息,则只能使用轮询或建立长连接的方法来实现,但是这样或浪费很多不必要的资源。而webSocket则解决了这个问题,通信可由双方发起,只需要建立一次连接,服务的端就可以持续从服务端获得消息。主要用来做消息通知,消息推送等模块
4、SpringBoot使用 STOMP 消息步骤
1)、添加pom文件依赖
2)、java方式配置websocket stomp
3)、消息实体类
4)、书写控制层
5)、书写页面
5、Pom 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
6、java方式配置websocket stomp
package com.example.demo.config; 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 { /** * 配置链接端点 * @param registry */ @Override public void registerStompEndpoints(StompEndpointRegistry registry){ registry.addEndpoint("/endpointWisely").withSockJS(); } /** * 配置消息代理 * @param registry */ @Override public void configureMessageBroker(MessageBrokerRegistry registry){ registry.enableSimpleBroker("/topic"); } }
7、消息实体类
package com.example.demo.PoJo; /** * 消息接受 */ public class WiselyMessage { private String name; public String getName(){ return name; } }
package com.example.demo.PoJo; /** * 消息返回 */ public class WiselyResponse { private String responseMessage; public WiselyResponse(String responseMessage){ this.responseMessage = responseMessage; } public String getResponseMessage(){ return responseMessage; } }
8、书写控制层
package com.example.demo.controller; import com.example.demo.PoJo.WiselyMessage; import com.example.demo.PoJo.WiselyResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; import java.security.Principal; @Controller public class WsController { /** * MessageMapping 类似于 RequestMapping * SendTo 订阅地址 类似于 订阅一个URL (我的理解就是 调用了这个方法 在返回的时候会给订阅该url的地址发送数据) * @param message * @return * @throws Exception */ @MessageMapping("/welcome") @SendTo("/topic/getResponse") public WiselyResponse say(WiselyMessage message) throws Exception { Thread.sleep(3000); return new WiselyResponse("Welcome," + message.getName() + "!"); } }
9、书写页面
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" /> <title>Spring Boot+WebSocket+广播式</title> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript> <div> <div> <button id="connect" onclick="connect();">连接</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button> </div> <div id="conversationDiv"> <label>输入你的名字</label><input type="text" id="name" /> <button id="sendName" onclick="sendName();">发送</button> <p id="response"></p> </div> </div> <script th:src="@{sockjs.min.js}"></script> <script th:src="@{stomp.min.js}"></script> <script th:src="@{jquery.js}"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected) { document.getElementById('connect').disabled = connected; document.getElementById('disconnect').disabled = !connected; document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden'; $('#response').html(); } function connect() { var socket = new SockJS('/endpointWisely'); //1 stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/getResponse', function(respnose){ //2 showResponse(JSON.parse(respnose.body).responseMessage); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendName() { var name = $('#name').val(); //3 stompClient.send("/welcome", {}, JSON.stringify({ 'name': name })); } function showResponse(message) { var response = $("#response"); response.html(message); } </script> </body> </html>
10、客户端发送和接收消息图解