springboot整合websocket的實現

https://www.cnblogs.com/strugglion/p/10021173.html

 

1.2 第二種實現方法

(1)WebSocket 業務邏輯實現。參數傳遞採用類似GET請求的方式傳遞,服務端的參數在攔截器中獲取之後通過attributes傳遞給WebSocketHandler。

複製代碼

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

@RestController
public class TestWebSocketController implements WebSocketHandler {
    
    private static AtomicInteger onlineCount = new AtomicInteger(0);
    
    private static final ArrayList<WebSocketSession> sessions = new ArrayList<>();
    
    private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketController.class);
    
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        int onlineNum = addOnlineCount();
        LOGGER.info("Oprn a WebSocket. Current connection number: " + onlineNum);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
        int onlineNum = subOnlineCount();
        LOGGER.info("Close a webSocket. Current connection number: " + onlineNum);
    }

    @Override
    public void handleMessage(WebSocketSession wsSession, WebSocketMessage<?> message) throws Exception {
        LOGGER.info("Receive a message from client: " + message.toString());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        LOGGER.error("Exception occurs on webSocket connection. disconnecting....");
        if (session.isOpen()) {
            session.close();
        }
        sessions.remove(session);
        subOnlineCount();
    }

    /*
     * 是否支持消息拆分發送:如果接收的數據量比較大,最好打開(true), 否則可能會導致接收失敗。
     * 如果出現WebSocket連接接收一次數據後就自動斷開,應檢查是否是這裏的問題。
     */
    @Override
    public boolean supportsPartialMessages() {
        return true;
    }

    
    public static int getOnlineCount() {
        return onlineCount.get();
    }
    
    public static int addOnlineCount() {
        return onlineCount.incrementAndGet();
    }
    
    public static int subOnlineCount() {
        return onlineCount.decrementAndGet();
    }

}

複製代碼

(2)HandShake 攔截器實現

複製代碼

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

public class TestHandShakeInterceptor extends HttpSessionHandshakeInterceptor {
    
    private final Logger LOGGER = LoggerFactory.getLogger(TestHandShakeInterceptor.class);
    
    /*
     * 在WebSocket連接建立之前的操作,以鑑權爲例
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, 
            WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        
        LOGGER.info("Handle before webSocket connected. ");
        
        // 獲取url傳遞的參數,通過attributes在Interceptor處理結束後傳遞給WebSocketHandler
        // WebSocketHandler可以通過WebSocketSession的getAttributes()方法獲取參數
        ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
        String id = serverRequest.getServletRequest().getParameter("id");
        String name = serverRequest.getServletRequest().getParameter("name");

        if (tokenValidation.validateSign()) {
            LOGGER.info("Validation passed. WebSocket connecting.... ");
            attributes.put("id", id);
            attributes.put("name", name);
            return super.beforeHandshake(request, response, wsHandler, attributes);
        } else {
            LOGGER.error("Validation failed. WebSocket will not connect. ");
            return false;
        }
    }
    
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
            WebSocketHandler wsHandler, Exception ex) {
        // 省略
    }

}

複製代碼

(3)WebSocket 配置類實現(註冊WebSocket實現類,綁定接口,同時將實現類和攔截器綁定)

複製代碼

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import TestWebSocketController;
import TestHandShakeInterceptor;

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

    @Autowired
    private TestWebSocketController testWebSocketController;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(TestWebSocketController, "/testWebSocket")
                .addInterceptors(new TestHandShakeInterceptor()).setAllowedOrigins("*");
    }

}

複製代碼

 

1.3 補充說明

(1)在WebSocket實現過程中,尤其是通過“@ServerEndpoint”實現的時候,可能會出現注入失敗的問題,即注入的Bean爲null的問題。可以通過手動注入的方式來解決,需要改造實現類和SpringBoot啓動類,如下:

複製代碼

@ServerEndpoint("testWebsocket")
@RestController
public class WebSocketController {

    private TestService testService;
    
    private static ApplicationContext applicationContext;
    
    @OnOpen
    public void onOpen(Session session) {
        testService = applicationContext.getBean(TestService.class);
    }

    @OnClose
    public void onClose() {}

    @OnMessage
    public void onMessage(String message, Session session) {}

    @OnError
    public void onError(Session session, Throwable error) {}

    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketController.applicationContext = applicationContext;
    }
    
}

複製代碼

複製代碼

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import WebSocketController;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
//        SpringApplication.run(Application.class, args);
        SpringApplication springApplication = new SpringApplication(Application.class);
        ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
        WebSocketController.setApplicationContext(configurableApplicationContext);  // 解決WebSocket不能注入的問題
    }

}

複製代碼

 

2. 客戶端的實現,我嘗試了html和java WebSocketClient兩種方式

2.1 html實現

複製代碼

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket示例</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport' />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
    <input id="text" type="text"/>
    <button οnclick="send()">發送消息</button>
    <hr/>
    <button οnclick="closeWebSocket()">關閉WebSocket連接</button>
    <hr/>
    <div id="message"></div>
</body>

<script type="text/javascript">
    var websocket = null;
    //判斷當前瀏覽器是否支持WebSocket
    if ('WebSocket' in window) {
        // 不帶參數的寫法
        websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket");
        // 通過路徑傳遞參數的方法(服務端採用第一種方法"@ServerEndpoint"實現)
        websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket/23/Lebron");
        // 通過類似GET請求方式傳遞參數的方法(服務端採用第二種方法"WebSocketHandler"實現)
        websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");
    }
    else {
        alert('當前瀏覽器 Not support websocket')
    }

    //連接發生錯誤的回調方法
    websocket.onerror = function () {
        setMessageInnerHTML("WebSocket連接發生錯誤");
    };

    //連接成功建立的回調方法
    websocket.onopen = function () {
        setMessageInnerHTML("WebSocket連接成功");
    }

    //接收到消息的回調方法
    websocket.onmessage = function (event) {
        setMessageInnerHTML(event.data);
    }

    //連接關閉的回調方法
    websocket.onclose = function () {
        setMessageInnerHTML("WebSocket連接關閉");
    }

    //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
    window.onbeforeunload = function () {
        closeWebSocket();
    }

    //將消息顯示在網頁上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //關閉WebSocket連接
    function closeWebSocket() {
        websocket.close();
    }

    //發送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

複製代碼

 

2.2 Java WebSocketClient實現

(1)WebSocketClient 實現類

複製代碼

import java.net.URI;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestWebSocketClient extends WebSocketClient {
    
    private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketClient.class);
    
    public TestWebSocketClient(URI serverUri) {
        super(serverUri);
    }
    
    public TestWebSocketClient(URI serverUri, Draft protocolDraft) {
        super(serverUri, protocolDraft);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        LOGGER.info("Open a WebSocket connection on client. ");
    }
    
    @Override
    public void onClose(int arg0, String arg1, boolean arg2) {
        LOGGER.info("Close a WebSocket connection on client. ");
    }

    @Override
    public void onMessage(String msg) {
        LOGGER.info("WebSocketClient receives a message: " + msg);
    }

    @Override
    public void onError(Exception exception) {
        LOGGER.error("WebSocketClient exception. ", exception);
    }

}

複製代碼

(2)WebSocketClient 發送數據

String serverUrl = "ws://127.0.0.1:18080/testWebsocket"
URI recognizeUri = new URI(serverUrl);
client = new TestWebSocketClient(recognizeUri, new Draft_6455());
client.connect();
client.send("This is a message from client. ");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章