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前端腳本。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章