websocket(二)–基於sockjs和stomp實現廣播通信

websocket(二)–基於sockjs和stomp實現廣播通信

一、簡介

原生websocket實現方式需要新版瀏覽器的支持。爲兼容不支持websocket的瀏覽器,可以使用sockjs和stomp,當瀏覽器不支持時,會自動改用輪詢等方式來模擬websocket實現。這裏介紹基於sockjs和stomp實現websocket的廣播通信。

二、知識點

2.1 sockjs

sockjs是javascript庫,提供了服務器和瀏覽器間的雙向通信,優先使用websocket通信,當瀏覽器不支持時,則自動改爲輪詢與服務器通信。

2.2、stomp

stomp(streaming text orientated messaging protocal),即面向流文本的消息協議,定義了服務端與客戶端進行消息傳遞的文本格式,其使用基於幀(frame)的格式定義消息。

三、後端知識點

3.1 註解@EnableWebSocketMessageBroker

開啓stomp協議,使用基於消息代理(message broker)來傳輸,消息的接收支持註解@MessageMapping;

3.2 WebSocketMessageBrokerConfigurer類

定義來自客戶端用於配置消息處理如stomp協議的方法,關鍵方法有:

  • default void registerStompEndpoints(StompEndpointRegistry registry)
    註冊endpoint, 即是客戶端連接址。如:registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();
  • default void configureMessageBroker(MessageBrokerRegistry registry)
    配置消息代理選項,如接收消息的地址前綴,如:registry.enableSimpleBroker("/topicTalkClient");
  • default void configureClientInboundChannel(ChannelRegistration registration)
    配置來自客戶端的用於接收消息的org.springframework.messaging.MessageChanne。
  • default void configureClientOutboundChannel(ChannelRegistration registration)
    配置發給客戶端的用於發送消息的org.springframework.messaging.MessageChanne。

3.3 @MessageMapping

服務端接收消息的地址(即是客戶端發送消息的地址)

3.4 @SendTo

服務端廣播的地址(即是客戶端監聽接收消息的地址);

四、前端知識點

  1. 需要下載sockjs.js和stomp.min.js

  2. 連接服務端,如:

    var socket = new SockJS("/endpoint");
    
  3. 獲取stomp對象,如:

    client = Stomp.over(socket);
    
  4. 連接並訂閱消息, 如:

    client.connect(headers, function (frame) {
            client.subscribe("/topicTalkClient", function (response) {..})
    });
    
  5. 發送消息,如:

    client.send("/topicTalkServer", headers, JSON.stringify({'uid': 1, 'msg': msg}));
    

五、使用示例

5.1 添加maven依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>

5.2 配置spring mvc類

package com.dragon.public_talk.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /
     * 添加靜態文件
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/").addResourceLocations("classpath:/static/");
    }
}

5.3 配置websocket類

package com.dragon.public_talk.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    /
     * 註冊stomp的endpoint端點
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //addEndpoint客戶端連接地址,setAllowedOrigins允許連接的域名,withSockJS支持以SockJS連接服務器
        registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();
    }

    /
     * 設置消息代理,默認會配置一個簡單的內存消息代理
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //配置發送給客戶端消息的地址前綴,這是"/topicTalkClient",可配置多個,對就@SendTo中的地址
        registry.enableSimpleBroker("/topicTalkClient");
    }
}

5.4 定義消息載體類

package com.dragon.public_talk.bean;
import lombok.Data;
/
 * 消息載體
 */
@Data
public class MsgBean {
    private String uid;
    private String msg;
}

5.5 定義轉發頁面和消息處理的controller

package com.dragon.public_talk.controller;

import com.dragon.public_talk.bean.MsgBean;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.*;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class TopicWebSocketController {
    /
     * 對話頁面
     *
     * @param model
     * @param uid
     * @return
     */
    @RequestMapping("/topicTalk/{uid}")
    public String topicTalk(Model model, @PathVariable String uid) {
        model.addAttribute("uid", uid);
        return "topicTalk";

    }

    /
     * 接收和廣播消息
     *
     * @param msg
     * @return
     */
    @MessageMapping("/topicTalkServer")  //客戶端發到服務端
    @SendTo("/topicTalkClient")  //服務端發到客戶端,客戶端訂閱
    public MsgBean topicTalk(MsgBean msg,  //消息
                             StompHeaderAccessor accessor, //所有消息頭信息
                             @Headers Map<String, Object> headers, //所有頭部值
                             @Header(name = "simpSessionId") String sessionId, //指定頭部的值 ,這裏指sessionId
                             Message message,   //完整消息,包含消息頭和消息體(即header和body)
                             @Payload String body) { //消息體內容
        //消息直接返回
        return msg;
    }
}

5.6 spring boot配置文件application.yaml

server:
  port: 8010

5.7 spring boot啓動類

package com.dragon.public_talk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PublicTalkApplication {

    public static void main(String[] args) {
        SpringApplication.run(PublicTalkApplication.class, args);
    }

}

5.8 添加前端靜態文件

添加前端靜態文件jquery-3.1.1.min.js、sockjs.js、stomp.min.js

5.9 定義前端頁面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        #historyId {
            border: 1px black solid;
            height: 370px;
            width: 500px;
            overflow-y: scroll;
            word-break: break-all;
        }

        #msgId {
            width: 500px;
            height: 130px;
        }

        #talkId {
            /*text-align: center;*/
        }
    </style>

</head>
<body>
<section id="talkId">
    <header>
        tip:<span id="tipId" th:text="${uid}"></span>
    </header>

    <article id="historyId">

    </article>
    <article>
        <textarea id="msgId"></textarea>
    </article>
    <footer>
        <button id="sendId">send</button>
        <button id="closeId">close</button>
        <button id="clearId">clear</button>
        <button id="connectId">connect</button>
    </footer>
</section>

<script src="../static/js/jquery-3.1.1.min.js"></script>
<script src="../static/js/sockjs.js"></script>
<script src="../static/js/stomp.min.js"></script>
<script type="text/javascript" th:inline="javascript">
    $(function () {
        //stomp協議客戶端對象
        var client;
        //連接
        var headers = {'uid': [[${uid}]]};
        var connect = function () {
            //連接endpoint地址,這裏完整地址爲: http://localhost:8010/endpoint
            var socket = new SockJS("/endpoint");
            //獲取stomp協議的客戶端對象
            client = Stomp.over(socket);
            client.connect(headers, function (frame) {
                //訂閱消息,對應後端@SendTo中地址
                client.subscribe("/topicTalkClient", function (response) {
                    var obj = JSON.parse(response.body)
                    $("#historyId").append("<p>" + obj.uid + ":" + obj.msg + "</p>");
                    $("#historyId").scrollTop($("#historyId")[0].scrollHeight);
                })
            });

            $("#tipId").html("connect success");
        };

        var sendMsg = function () {
            var msg = $("#msgId").val();
            if (/^\s*$/.test(msg)) {
                $("#msgId").val("");
                return;
            }
            //發送消息,對應@MessageMapping中地址
            client.send("/topicTalkServer", headers, JSON.stringify({'uid': [[${uid}]], 'msg': msg}));
            $("#msgId").val("");
        };

        //點按鈕發送消息
        $("#sendId").click(function () {
            sendMsg();
        });

        //回車發消息
        $(document).keyup(function (e) {
            if (e.keyCode == 13) {
                sendMsg();
            }
        });

        //清空記錄
        $("#clearId").click(function () {
            $("#historyId").empty();
        });

        //連接
        $("#connectId").click(function () {
            connect();
        });

        //關閉連接
        $("#closeId").click(function () {
            client.disconnect();
            $("#tipId").html("connect close");
        });

    })
</script>
</body>
</html>

至此,示例websocket廣播結束。
訪問地址:
http://localhost:8010/topicTalk/1
http://localhost:8010/topicTalk
/2
所在頁面均會收到消息

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