springboot-集成WebSockets廣播消息


一 WebScoketS 簡介

RFC 6455 即 webSockets 協議提供了一種標准化的方式去建立全雙工,雙方面交流的通道在客戶端和服務端甚至單一的TCP連接中進行通信; webSockets 協議其跟HTTP的tcp協議不同,但是其設計目的是通過HTTP協議進行工作,可以使用40或者443端口和重新使用現有的防火牆規則;

GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket 
Connection: Upgrade 
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080

webSockets 的交互是以HTTP協議開始的,使用Upgrade header 轉向使用Upgrade連接;如果非200狀態成功響應就類似於下面的信息;如果WebSocket server 是運行在nginx是需要配置WebSocket upgrade requests ;如果是運行在雲上,需要查閱相關的雲是否支持WebSocket ;

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp

二 HTTP 和 WebSocket 對比

  1. 在HTTP和REST中一個應用需要很多的URLs;應用和客戶端是通過 請求和響應的風格使用這些URLs進行交互;服務端會路由這些請求給基於HTTP 的URL或者方法或者頭進行處理;
  2. WebSockets 通常在初始化的時候就只有一個鏈接,所有應用的消息都是通過相同的TCP連接進行流動;這指向一個完全不同的異步、事件驅動的消息傳遞體系結構。
  3. WebSocket 是一種低端的協議,不像HTTP,其不規定消息中內容中任何的語義信息;這意味着其沒有任何方式去路由或者處理這些信息,除非客戶端和服務端在語義上達成一致;
  4. WebSocket 客戶端和服務端其交流是通過使用更加高級的消息協議(比如STOMP)和基於HTTP握手請求的Sec-WebSocket-Protocol header ;

三 注意事項

WebSockets可以使web頁面具有動態性和交互性。然而,在許多情況下,Ajax和HTTP流或 long polling(輪詢)可以提供一個簡單有效的解決方案。HTTP流和polling適用於消息不頻繁的交互,WebSockets適用於消息較頻繁的交互;在 因特網上由於沒有Upgrade header 或者 關閉了空閑的長鏈接,受限於在你有限的代理可能會將WebSockets的交互排除;

四 websocket配置和依賴

4.1 依賴

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/>
    </parent>
    
	<dependencies>
        <!--  websocket依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!-- 模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

4.2 配置

/**
 * @Author lsc
 * @Description <p>websocket配置類 </p>
 * @Date 2019/11/12 22:27
 */
//使用STOMP協議來傳輸基於消息代理的消息,控制器支持在@Controller類中使用@MessageMapping
@EnableWebSocketMessageBroker
@Configurable
@EnableWebSocket
@Component
public class WebConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注冊 Stomp的端點(Endpoint),並且映射指定的url
        registry.addEndpoint("/websocket")
                .setAllowedOrigins("*") // 添加允許跨域訪問
                .withSockJS();// 指定SockJS協議
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 啟動廣播模式代理,只有符合的的路徑才發送消息
        registry.enableSimpleBroker("/topic");
    }
}

五 實體類

5.1 接受消息實體

/**
 * @Author lsc
 * @Description <p> 接受客戶端消息</p>
 * @Date 2019/11/12 22:42
 */
public class AcceptMessages {

    private String name;

    public String getName() {
        return name;
    }
}

5.2 發送消息實體

/**
 * @Author lsc
 * @Description <p>發送消息給客戶端 </p>
 * @Date 2019/11/12 22:42
 */
public class SendMessages {

    private String responseMessage;

    public String getResponseMessage() {
        return responseMessage;
    }

    public void setResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }
}

六控制器

/**
 * @Author lsc
 * @Description <p>websockets 之 廣播式</p>
 * @Date 2019/11/12 22:49
 */
@Controller
public class WebSocketsController {

    @MessageMapping("/welcome")//類似@RequestMapping,進行客戶端請求地址映射
    @SendTo("/topic/getResponse")//訂閱了@SendTo中的路徑進行發送消息
    public SendMessages broadcast(AcceptMessages acceptMessages){
        System.out.println(acceptMessages.getName());
        SendMessages sendMessages = new SendMessages();
        sendMessages.setResponseMessage("知識追尋者:"+acceptMessages.getName());
        return sendMessages;
    }

}

七 前端頁面

在 resource目錄下新建templates目錄存放WebSockets.html;在resource目錄下新建static目錄,繼續在其子目錄下新建js目錄存放sockjs.min.js,stomp.min.js,jquery-3.3.1.min.js;

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>springboot廣播式WebSocket</title>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ffff0000;">Sorry,not support the 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="@{js/sockjs.min.js}"></script>
<script th:src="@{js/stomp.min.js}"></script>
<script th:src="@{js/jquery-3.3.1.min.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() {
        // 轉向 endpoint 名為websocket
        var socket = new SockJS('/websocket');
        // 使用ssocket的協議
        stompClient = Stomp.over(socket);
        // 連接
        stompClient.connect({}, function (frame) {
            setConnected(true);
            console.log('Connected:' + frame);
            // @Sendto 中定義路徑 向目標訂閱消息
            stompClient.subscribe('/topic/getResponse', function (response) {
                showResponse(JSON.parse(response.body).responseMessage);
            })
        });
    }
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log('Disconnected');
    }
    function sendName() {
        var name = $('#name').val();
        // 控制器@MessageMapping中定義向目標發送消息
        stompClient.send("/welcome", {}, JSON.stringify({'name': name}));
    }
    function showResponse(message) {
        $("#response").html(message);
    }
</script>
</body>
</html>

八 視圖轉發

當客戶端請求地址是localhost:8080/ws,經過springmvc視圖轉發器至WebSockets.html;

/**
 * @Author lsc
 * @Description <p> spingmvc視圖映射轉發</p>
 * @Date 2019/11/12 23:35
 */
@Configurable
@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 配置視圖轉發
        registry.addViewController("/ws").setViewName("/WebSockets");
    }
}

九 效果圖

即一個瀏覽器發送消息,其它連接的瀏覽器也能收到消息,即廣播形式;
在這里插入圖片描述

十參考文獻

參考1:《JavaEE開發的顛覆者》
參考2: spring-web
源碼: youku1327


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM