• spring-boot之websocket · 下


    前言

    昨天我们提到,并不是所有的浏览器都支持websokcet协议,对于不支持的浏览器,我们要通过STOMP 协议来进行兼容,今天我们就来看下如何通过STOMP 来兼容websocket

    websocket兼容

    STOMP的全称是Simple (or Streaming) Text Orientated Messaging Protocol,中文的意思是简单(流)文本定向消息协议,也就是说,我们其实使用了消息组件来兼容的。

    配置类

    对于不支持websocket的浏览器我们需要通过STOMP来兼容,兼容的解决方案涉及两方面知识,一个是SockJs,一个就是WebSocketMessageBrokerSockJs一种让前端可以支持socket通信的技术解决方案,WebSocketMessageBroker是基于消息组件实现的一种通信协议。

    下面是我们的STOMP解决方案的配置类,注释已经够详细了,所以这里就不在赘述。

    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
        /**
         * 注册服务器端点
         * @param registry
         */
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            // 增加一个聊天服务端点
            registry.addEndpoint("/socket").withSockJS();
            // 增加一个用户服务端点
            registry.addEndpoint("/wsuser").withSockJS();
        }
    
        /**
         * 定义服务器端点请求和订阅前缀
         * @param registry
         */
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            // 客户端订阅路径前缀
            registry.enableSimpleBroker("/sub", "/queue");
            // 服务端点请求前缀
            registry.setApplicationDestinationPrefixes("/request");
        }
    }
    

    消息接收接口

    这里定义两个接口,一个是接收通用消息的(/send),一个是发给指定用户的(/sendToUser)。这里需要补充说明的是,@SendTo注解的作用是将接收到的消息发送到指定的路由目的地,所有订阅该消息的用户都能收到,属于广播。

    @RestController
    public class WebsocketController {
        private final Logger logger = LoggerFactory.getLogger(WebsocketController.class);
    
        @Autowired
        private SimpMessagingTemplate simpMessagingTemplate;
    
        @Autowired
        private WebSocketService webSocketService;
    
        @MessageMapping("/send")
        @SendTo("/sub/chat")
        public String sendMessage(String value) {
            logger.info("发送消息内容:{}", value);
            return value;
        }
    
        @MessageMapping("/sendToUser")
        public void sendToUser(Principal principal, String body) {
            String srcUser = principal.getName();
            String[] args = body.split(": ");
            String desUser = args[0];
            String message = String.format("【%s】给你发来消息:%s", webSocketService.getNameMap().get(srcUser), args[1]);
            // 发送到用户和监听地址
            simpMessagingTemplate.convertAndSendToUser(desUser, "/queue/customer", message);
    
        }
    
    }
    

    前端页面

    普通消息发送

    首先要引入jquery.jsstomp.jssockjs.js,这个三个js就可以确保前端页面也支持STOMP协议。

    然后我们定义了三个方法:connect()disconnect()sendMessage()方法。

    connect方法内部,我们通过SockJS初始化了stompClient实例,SockJS的节点地址就是我们配置类中定义的聊天服务节点,然后建立stomp连接。

    发送消息的时候,我们直接调用stomp客户端的send方法即可,这里需要指定发送消息的地址,要和消息接收方的地址一致。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>websocket STOMP</title>
    </head>
    <body>
    websocket兼容STOMP测试<br>
    <div>
        <div>
            <button id = "connect" onclick="connect()">连接</button>
            <button id = "disconnect" disabled="disabled" onclick="disconnect()">断开连接</button>
    </div>
        <div id = "conversationDiv">
            <p>
                <label>发送消息内容</label>
            </p>
            <p>
                <textarea id="message" rows = "5"></textarea>
            </p>
            <p>
                <button id = "sendMsg" onclick="sendMessage()">发送</button>
            </p>
            <p id = "response">
    
            </p>
        </div>
    
        <a href="#" target="/websocket-receive">跳转到消息接收页</a>
    </div>
    
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
    <script type="text/javascript">
        var stompClient = null;
        // 设置连接
        function setConnected(connected) {
            $("#connect").attr({"disabled": connected});
            $("#disconnect").attr({"disabled": !connected});
            if (connected) {
                $("#conversationDiv").show();
            } else {
                $("#conversationDiv").hide();
            }
            $("#response").html("")
        }
    
        function connect() {
            // 定义请求服务器的端点
            var socket = new SockJS('/socket');
            // stomp客户端
            stompClient = Stomp.over(socket);
            // 连接服务器端点
            stompClient.connect({}, function (frame) {
                // 建立连接后的回调
                setConnected(true);
            })
        }
        // 断开socket连接
        function disconnect() {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");
        }
        // 向/request/send服务端发送消息
        function sendMessage() {
            var message = $("#message").val();
            // 发送消息到"/request/send",其中/request是服务器定义的前缀
            // 而/send则是@MessageMapping所配置的路径
            stompClient.send("/request/send", {}, message);
        }
        connect();
    </script>
    
    </body>
    </html>
    
    普通文本消息接收

    接收页面和发送页面对应,sockJS的地址必须一样,因为是接收消息,所以这里执行的是stompClientsubscribe(订阅消息),这里的地址也必须和发送页面一致,否则无法收到消息

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>websocket-stomp-receive</title>
    </head>
    <body>
    
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
    <script type="text/javascript">
        var noticeSocket = function () {
            // 连接服务器端点
            var s = new SockJS('/socket');
            //客户端
            var stompClient = Stomp.over(s);
            stompClient.connect({}, function () {
                console.log("notice socket connected !");
                // 订阅消息地址
                stompClient.subscribe('/sub/chat', function (data) {
                    $('#receive').html(data.body);
                });
            });
        };
        noticeSocket();
    </script>
    <h1><span id="receive">等待接收消息</span></h1>
    
    </body>
    </html>
    
    普通文本测试

    我登陆了两个账号,用其中一个账号发送消息,他自己以及另一个账号都收到了发送的消息,说明我们的实例是ok的。

    下面,我们看下如何给指定用户发送消息。

    给指定用户发送消息

    发送页面没有区别,只是js不一样,所以这里只贴出js

    首先第一个不一样的地方是服务端点不一样了,我们这里的SockJS监听的是/wsuser,也就是给指定用户发送消息的地址。

    然后再就是发送消息的地址也变了,指定的是/request/sendToUser,对应的是指定用户的发送消息的接口,剩下其他的都一模一样。

    <script type="text/javascript">
    
    
        var stompClient = null;
        // 设置连接
        function setConnected(connected) {
            $("#connect").attr({"disabled": connected});
            $("#disconnect").attr({"disabled": !connected});
            if (connected) {
                $("#conversationDiv").show();
            } else {
                $("#conversationDiv").hide();
            }
            $("#response").html("")
        }
    
        function connect() {
            // 定义请求服务器的端点
            var socket = new SockJS('/wsuser');
            // stomp客户端
            stompClient = Stomp.over(socket);
            // 连接服务器端点
            stompClient.connect({}, function (frame) {
                // 建立连接后的回调
                setConnected(true);
            })
        }
        // 断开socket连接
        function disconnect() {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");
        }
        // 向/request/send服务端发送消息
        function sendMessage() {
            var message = $("#message").val();
            var user = $("#user").val();
            // 发送消息到"/request/send",其中/request是服务器定义的前缀
            // 而/send则是@MessageMapping所配置的路径
            var messageSend = user + ": " + message
            stompClient.send("/request/sendToUser", {}, messageSend);
        }
        connect();
    </script>
    
    给指定用户接收页面

    这里也只是js发生变化,节点名称和发生页面一致,订阅地址和配置类中的一致。

    <script type="text/javascript">
        var noticeSocket = function () {
            // 连接服务器端点
            var s = new SockJS('/wsuser');
            //客户端
            var stompClient = Stomp.over(s);
            stompClient.connect({}, function () {
                console.log("notice socket connected !");
                // 订阅消息地址
                stompClient.subscribe('/user/queue/customer', function (data) {
                    $('#receive').html(data.body);
                });
            });
        };
        noticeSocket();
    </script>
    
    给指定用户发送消息测试

    这次我们用哪吒的账号给女娲发了一条消息,最终的结果是只有女娲收到了消息,也和我们预期一致。

    总结

    相比于昨天我们直接通过websocket通信,通过STOMP通信,前端要稍过复杂一些,但总体来说,也不是特别复杂。

    通篇来看,其实STOMP就是后端启动一个消息池,然后将消息发送接口暴露给前端,前端调用发送消息接口发消息,消息由后端转发到消息池中指定的队列(类似消息中继站),然后消费者(订阅该队列的消息接收方)接收并消费其中的消息。

    如果知道了这点,那我们完全可以自己根据mq的相关文档开发一套,而且现在好多mq都提供了对ajax的支持,比如activemq

  • 相关阅读:
    【转】Android开发——MediaProvider源码分析(2)
    关于ActivityGroup使用过程中遇到的一点问题
    HttpWebRequest详解
    关于Assembly.CreateInstance()与Activator.CreateInstance()方法的区别
    你会在C#的类库中添加web service引用吗?
    ASP.NET跳转网页的三种方法的比较
    .net发送HTTP POST包
    依赖注入
    微软ASP.NET MVC Beta版本发布
    随笔~
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/15061248.html
Copyright © 2020-2023  润新知