1. 程式人生 > >springboot2.0 整合WebSocket(服務端實時向客戶端傳送資訊)

springboot2.0 整合WebSocket(服務端實時向客戶端傳送資訊)

     WebSocket為瀏覽器和服務端提供了雙工非同步通訊功能。即瀏覽器可以向服務端傳送訊息,服務端也可以向瀏覽器傳送訊息。

    應用於web專案中,多數情況前端需要實時的資料獲取。即服務端向客戶端實時的傳送訊息。

操作步驟如下:

專案目錄如下:


1、運用IDE新建一個springboot專案,勾選Web、WebSocket、Thymeleaf等依賴如下圖:


2、編寫前端頁面(客戶端)與後臺(服務端)通訊握手攔截器。此處可記錄當前建立連線的使用者資訊,用於定向資訊傳送的動能前提。程式碼如下

package com.example.websocket_springboot.config.websocket;
import 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); } }
3、編寫WebSocket配置類,比如繫結客戶端端點url
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前端指令碼。