springboot2.0 整合WebSocket(服務端實時向客戶端傳送資訊)
阿新 • • 發佈:2019-02-11
WebSocket為瀏覽器和服務端提供了雙工非同步通訊功能。即瀏覽器可以向服務端傳送訊息,服務端也可以向瀏覽器傳送訊息。
應用於web專案中,多數情況前端需要實時的資料獲取。即服務端向客戶端實時的傳送訊息。
操作步驟如下:
專案目錄如下:
1、運用IDE新建一個springboot專案,勾選Web、WebSocket、Thymeleaf等依賴如下圖:
2、編寫前端頁面(客戶端)與後臺(服務端)通訊握手攔截器。此處可記錄當前建立連線的使用者資訊,用於定向資訊傳送的動能前提。程式碼如下
package com.example.websocket_springboot.config.websocket;3、編寫WebSocket配置類,比如繫結客戶端端點urlimport org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import java.util.Map; /** * @description 前端頁面與後臺通訊握手攔截器,可用於完善定向傳送資訊功能。 * @author jane */ @Component public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,Map<String, Object> attributes) throws Exception { //此處可將使用者資訊放入WebSocketSession的屬性當中,以便定向傳送訊息。 attributes.put("WEBSOCKET_USERID",1L); return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }
package com.example.websocket_springboot.config.websocket; import com.example.websocket_springboot.handler.DemoHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; /** * @description webSocket配置類,繫結前端連線端點url及其他資訊 * @author jane */ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private DemoHandler demoHandler; @Autowired private HandshakeInterceptor handshakeInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //繫結前端連線端點url registry.addHandler(demoHandler, "/webSocket/demo").addInterceptors(handshakeInterceptor).withSockJS(); } }
4、自定義實現WebSocket處理類
package com.example.websocket_springboot.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.socket.*; import java.util.ArrayList; /** * @description webSocket處理類 * @author jane */ @Component public class DemoHandler implements WebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(DemoHandler.class); private static final ArrayList<WebSocketSession> users; static { users = new ArrayList<>(); } @Override public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception { logger.info("系統WebSocket連線已建立!"); //此處可新增客戶端接收使用者 logger.info(webSocketSession.getAttributes().get("WEBSOCKET_USERID").toString()); users.add(webSocketSession); } /** * 給指定使用者傳送資訊 * @param webSocketSession * @param webSocketMessage * @throws Exception */ @Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception { //獲取指定使用者ID Long userId = (Long) webSocketSession.getAttributes().get("WEBSOCKET_USERID"); String message; logger.info("處理推送的訊息"); //判斷客戶端是否訊息傳送,不需要客戶端與客戶端的單向通訊,此處可省略。 if (!webSocketMessage.getPayload().equals("undefined")){ message = "client 傳送的訊息為:" + webSocketMessage.getPayload(); }else { message = "推送測試資訊 ---" + System.currentTimeMillis(); } sendMessageToUser(userId, new TextMessage(message)); } @Override public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception { if (webSocketSession.isOpen()) { webSocketSession.close(); } logger.error("系統WebSocket傳輸錯誤,連線關閉!使用者ID:" + webSocketSession.getAttributes().get("WEBSOCKET_USERID"), throwable); //移除異常使用者資訊 users.remove(webSocketSession); } @Override public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception { } @Override public boolean supportsPartialMessages() { return false; } public void sendMessageToUser(Long userId, TextMessage message) { logger.info("傳送訊息至使用者!"); for (WebSocketSession user : users) { if (user.getAttributes().get("WEBSOCKET_USERID").equals(userId)) { sendSocketSessionMsg(user, message); } } } /** * 傳送訊息 * @param user 接收使用者 * @param message 訊息 */ private boolean sendSocketSessionMsg(WebSocketSession user, TextMessage message) { String msg = message.getPayload(); boolean sendSuccess = true; try { if (user.isOpen()) { synchronized (user) { user.sendMessage(message); } } else { logger.error("WebSocket連線未開啟,系統訊息推送失敗:" + msg); sendSuccess = false; } } catch (Exception e) { logger.error("系統訊息推送失敗:" + msg, e); sendSuccess = false; } return sendSuccess; } }
5、配置MVC頁面訪問的配置類
package com.example.websocket_springboot.config.mvc; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description 配置MVC頁面訪問 * @author jane */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { /** * 設定頁面訪問 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/demo").setViewName("/demo"); } /* *//** * 配置靜態訪問資源 * @param registry *//* @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); }*/ }
6、前端新增demo.html頁面,程式碼如下:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" > <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>測試webSocket</title> </head> <body> <div>訊息</div> <div id="message"></div> <div> <input id="msg"> <button onclick="sendMsg()">傳送訊息</button> </div> </body> <script th:src="@{../static/plugins/sockjs.min.js}"></script> <script th:src="@{../static/plugins/jquery.min.js}"></script> <script th:src="@{../static/js/demo.js}"></script> </html>
js檔案如下:
var webSocket= null; $(function() { test(); }); function test() { webSocket = new SockJS("/webSocket/demo"); webSocket.onopen = function () { webSocket.send(); }; webSocket.onmessage = function (e) { if (e.data) { var data = e.data; $("#message").text(data); } }; webSocket.onclose = function () { console.log('close run status socket'); }; webSocket.onerror = function () { console.error("連結出現異常,請檢查伺服器是否正常執行"); }; } function sendMsg() { if (webSocket.readyState == SockJS.OPEN){ var msg = $("#msg").val(); webSocket.send(msg); }else { alert("連線失敗!") } }
自行新增sockjs.min.js和jquery.min.js前端指令碼。