SpringBoot 整合 WebSocket
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、P om 依賴
<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>
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"); } }
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; } }
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() + "!"); } }
<!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、客戶端傳送和接收訊息圖解