美文网首页
Springboot 技术整合--笔记6--集成websocke

Springboot 技术整合--笔记6--集成websocke

作者: 牵手生活 | 来源:发表于2019-05-29 11:28 被阅读0次

WebSocket的ping与pong的java实现…

理解几个知识点

  • 订阅地址 :如stompEndpointRegistry.addEndpoint("/endpointWechat") 或使用@ServerEndpoint创建
  • 容许跨域
  • 是否开启SockJS支持
  • 推送:声明SimpMessagingTemplate ,调用convertAndSend方法(或者使用@SendTo和@SendToUser注解)
  • 一对一发送消息
  • Principal:身份验证和授权
  • stomp协议:STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。
  • WebSocket(stomp服务端)
websocket.jpg

WebSocketSession 发送的消息类型(sendMessage)

前端涉及js

  • jquery
  • sockJs :
  • stomp.min.js(stomp客户端)
  • STOMP Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互

js 前端链接websocket的方式

STOMP 链接成功、失败、主动断开等参阅
STOMP 客户端 API 整理:https://blog.csdn.net/jqsad/article/details/77745379

html页面,连接stomp服务端,订阅/topic/myTop的消息(订阅topic或myTop)


方式1:
var socket = new SockJS('/endpointWechat'); 
方式2
    //前台 js 中 new SockJS 的时候,一起是3个参数项的,另外2个参数项可以传递目前是哪个用户的标识,这样就可以在后台区分出来连接是哪个用户建立的
    var socket = new SockJS(url, undefined, {protocols_whitelist: ['websocket']});

方式3:直接携带参数
var socket = new SockJS('/endpointWechat'+ '?token='+str_token); //'?token=token8888'

Android端的链接方式

SpringBoot 使用的websocket 协议,不是标准的websocket协议,使用的是名称叫做STOMP的协议。
要想与js方式调用:stompClient.send("/sendServer", {}, JSON.stringify({ 'name': message }));,需要Android采用STOMP方式调用

更多细节参考
stomp协议 官方:http://stomp.github.io/
csdn 大神博客:http://blog.csdn.net/chszs/article/details/5200554
iteye 大神博客 http://diaocow.iteye.com/blog/1725186 (务必看一下,了解协议的一些使用)
SpringBoot webSocket 发送广播、点对点消息,Android接收

WebSocket可以应用于即时通信等场景,比如现在直播很火热,直播中的弹幕也可以使用WebSocket去实现。
WebSocket的协议内容可以见 The WebSocket Protocol,讲得最全面的官方说明。简单介绍可以见维基百科WebSocket
在Android客户端,一般可以使用下面的库完成WebSocket通信

  • okhttp,一般人我不告诉他okhttp还可以用来进行WebSocket通信
  • Java-WebSocket,纯java实现的WebSocket客户端和服务端实现
image.png

Android最佳实践——深入浅出WebSocket协议:https://blog.csdn.net/blueangle17/article/details/80701152


springboot 的工程


image.png

pom.xml引入

        <!--添加websocket依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <!--添加alibaba 的fastjson引用-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.46</version>
        </dependency>

编写MyHandShakeInterceptor类(主要作用用于对握手前检查合法性)

// 初始化对象,拦截握手,发生在链接之前
@Component
public class MyHandShakeInterceptor implements HandshakeInterceptor {

    private static final Logger log = LoggerFactory.getLogger(MyHandShakeInterceptor.class);
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {


        //http://localhost:8080/endpointWechat/948/dtdzvrrs/websocket?token=token8888
        //js调用:
        //        var host="http://localhost:8080";
        //        var socket = new SockJS(host+'/endpointWechat' + '?token=token8888');

        log.info("this.getClass().getCanonicalName() = {},在这里决定是否允许链接,http协议转换websoket协议进行前, 握手前Url = {}",this.getClass().getCanonicalName(),request.getURI());


        //System.out.println(this.getClass().getCanonicalName() + " 在这里决定是否允许链接,http协议转换websoket协议进行前, 握手前"+request.getURI() );


        // http协议转换websoket协议进行前,可以在这里通过session信息判断用户登录是否合法
        //request.getURI().getPath(); //   /endpointWechat/896/mdoqjqia/websocket
        //request.getURI().getHost();//localhost
        //request.getURI().string  ; //  http://localhost:8080/endpointWechat/896/mdoqjqia/websocket?token=token8888
        //request.getURI().getQuery() ;  // token=token8888
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
            HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();

            String myToken = httpRequest.getParameter("token");
            if (null != myToken && !StringUtils.isEmpty(myToken)){
                WebSocketSession webSocketSession = SocketManager.get(myToken);
                if (webSocketSession != null){
                    log.info("token = {},已经在建立链接列表,不允许重复链接",myToken);

                }else {
                    return true;

                }
            }



        }

        return false;  //不允许建立链接

        //return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
        //握手成功后,
        System.out.println(this.getClass().getCanonicalName() + "握手成功后...");
    }
}

不允许创建连接的情况

成功连接的情况


成功连接的情况

配置类WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Autowired
    private MyHandShakeInterceptor myHandShakeInterceptor;
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //以 /endpointWechat端点,客户端就可以通过这个端点来进行连接
        stompEndpointRegistry.addEndpoint("/endpointWechat").setAllowedOrigins("*")
                .withSockJS();//开启SockJS支持
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic","/user"); //客户端订阅服务的前缀
        registry.setUserDestinationPrefix("/user");  //开启一对一发送消息
    }

}

控制器WebScoketController


/**
 * 创建人:牵手生活
 * 创建时间:2019-01-14 17:17
 */

@Controller

public class WebSocketController {
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;  //声明SimpMessagingTemplate (或者使用@SendTo和@SendToUser注解),SimpMessagingTemplate可以在需要用到推送的地方如Controller,service,Component等地方

    private static final Logger log = LoggerFactory.getLogger(WebSocketController.class);



    // 收到消息记数
    private AtomicInteger count = new AtomicInteger(0);




    /**
     * @MessageMapping 指定要接收消息的地址,类似@RequestMapping。除了注解到方法上,也可以注解到类上
     * @MessageMapping("/receive") 对应html中的  stompClient.send("/app/receive", {}, JSON.stringify({ 'name': name }));
     * 多出来的“/app"是WebSocKetConfig中定义的,如不定义,则HTML中对应改为stompClient.send("/receive")
     * @SendTo默认 消息将被发送到与传入消息相同的目的地
     * 消息的返回值是通过{@link org.springframework.messaging.converter.MessageConverter}进行转换
     * @SendTo("/topic/getResponse") 指定订阅路径,对应HTML中的stompClient.subscribe('/topic/getResponse', ……)
     * 意味将信息推送给所有订阅了"/topic/getResponse"的用户
     * @param requestMessage
     * @return
     */
    @MessageMapping("/receive")
    @SendTo("/topic/getResponse")  //topic是广播全局通讯
    public ResponseMessage receive(RequestMessage requestMessage){
        log.info("receive message = {}" , JSONObject.toJSONString(requestMessage));
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setResponseMessage("响应消息WebSocketController receive [" + count.incrementAndGet() + "] records:"+JSONObject.toJSONString(requestMessage));
        return responseMessage;
    }



    /**
     * 客户端发消息,服务端接收
     *
     * @param requestMessage
     */
    // 相当于RequestMapping
    @MessageMapping("/sendServer")
    public void sendServer(RequestMessage requestMessage) {
        log.info("sendServer 客服端发送的,不需要发回给客户端message:{}", JSONObject.toJSONString(requestMessage));
    }

    @MessageMapping("/sendServer_str")
    public void sendServer_str(String message) {
        log.info("sendServer 客服端发送的,不需要发回给客户端message:{}", message);
    }



    /**
     * 客户端发消息,大家都接收,相当于直播说话
     *
     * @param message
     * @return
     */
    @MessageMapping("/sendAllUser_str")
    @SendTo("/topic/sendTopic_str")
    public String sendAllUser_str(String message) {
        // 也可以采用template方式
        return "服务的处理后的:"+message;
    }

    @MessageMapping("/sendAllUser")
    @SendTo("/topic/sendTopic")
    public ResponseMessage sendAllUser(RequestMessage requestMessage) {
        log.info("sendTopic 请求message = {}" , JSONObject.toJSONString(requestMessage));
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setResponseMessage(JSONObject.toJSONString(requestMessage));
        return responseMessage;
    }



    /**
     * 点对点用户聊天,这边需要注意,由于前端传过来json数据,所以使用@RequestBody
     * 这边需要前端开通var socket = new SockJS(host+'/myUrl' + '?token=token8888');   token为指定name
     * @param map
     */
    @MessageMapping("/sendMyUser")
    public void sendMyUser(@RequestBody Map<String, String> map) {
        log.info("sendMyUser 请求 map = {}", map);
        WebSocketSession webSocketSession = SocketManager.get(map.get("name"));
        if (webSocketSession != null) {
            log.info("sendMyUser sessionId = {}", webSocketSession.getId());

            //生成IJSONResult对象的data数据
            ResponseMessage responseMessage = new ResponseMessage();
            responseMessage.setResponseMessage("响应消息WebSocketController sendMyUser  records:"+map.get("message"));
            simpMessagingTemplate.convertAndSendToUser(map.get("name"), "/queue/sendUser", IJSONResult.ok(responseMessage));

            //simpMessagingTemplate.convertAndSendToUser(map.get("name"), "/queue/sendUser", JSONObject.toJSONString(responseMessage));  //ok
        }
    }

    @MessageMapping("/sendMyUser_obj")
    //@SendToUser("/user/queue/sendUser_obj")  //添加看看
    public ResponseMessage sendMyUser_obj(RequestMessage requestMessage) {
        log.info("sendMyUser message = {}" , JSONObject.toJSONString(requestMessage));
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setResponseMessage("响应消息WebSocketController sendMyUser [" + count.incrementAndGet() + "] records:"+JSONObject.toJSONString(requestMessage));

        return responseMessage;
    }



    //http://localhost:8080//wechatTask/websocket/index 转发到页面
    @RequestMapping(value="/wechatTask/websocket/index")
    public String websocketIndex(HttpServletRequest req){

        log.info("websocketIndex接口的 req.getRemoteHost(){}" , req.getRemoteHost());
        return "websocket/simple/websocket-index";
    }
}


添加thymeleaf的模板-websocket-index.html

说明
客户端可以通过使用Stomp.js和sockjs-client连接

var socket = new SockJS('/endpointWechat'+ '?token='+str_token);  


socket连接对象也可通过WebSocket(不通过SockJS)连接

var socket=new WebSocket('/endpointWechat'+ '?token='+str_token);

stompClient.connect()方法签名:

client.connect(headers, connectCallback, errorCallback);

websocket-index.html如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring Boot+WebSocket例子</title>
    <script src="https://cdn.bootcss.com/sockjs-client/1.3.0/sockjs.min.js"></script>
    <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <!--引入jqurey库-->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<body  onload="disconnect()" >

<div>
    <p> 创建链接:var socket = new SockJS(host+'/myUrl' + '?token=token8888')</p>
    <p>     token 是你上面输入的token 或username</p>


    <p>点击链接,如果链接成功,则可以与后台通过websocket进行通信 </p>
    <p>输入你要发送的内容(这里是你的名字--以后改为json对象)</p>
    <p>点击发送,会把输入的内容通过websocket发送到后台</p>

    <p>查看log 更多细节查看chrome的开发者选项中的控制台</p>

</div>


<div >
    <label >输入你新建链接是的token或name(用于点对点通信)?</label>
    <input type="text" id="mytoken"  value="wxid_on8oksh88zo22" placeholder="Your token/name here...如:4567"></input>
</div>
<div >
    <label id = "state-info" >***连接状态-未连接***</label>

</div>
</div>

<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="message"  value="fdsfs"/>
        <button id="sendName" onclick="sendName();">发送</button>
        <button id="sendServer" onclick="sendServer();">发送sendServer</button>
        <button id="sendTopic" onclick="sendTopic();">发送sendTopic</button>
        <button id="sendMyUser" onclick="sendMyUser();">发送点对点</button>
        <!--用于显示通过websocket的响应数据-->
        <p id="response"></p>
    </div>
</div>


<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() {
        //判断是否输入token
        var val = $('#mytoken').val();
        var str_token = val.replace(/(^\s*)|(\s*$)/g, '');//去除空格;
        if (str_token == '' || str_token == undefined || str_token == null){
            alert('建立链接前,需要输入token');
            return
        }


        // websocket的连接地址,此值等于WebSocketMessageBrokerConfigurer中registry.addEndpoint("/endpointWechat").withSockJS()配置的地址
        //var socket = new SockJS('/endpointWechat'); //
        //建立连接对象(还未发起连接)
        var socket = new SockJS('/endpointWechat'+ '?token='+str_token); //'?token=token8888'
        //利用Stomp协议创建socket客户端
        stompClient = Stomp.over(socket);

        /**
         * 调用stompClient中的connect方法来连接服务端,
         * 连接成功之后调用setConnected方法,该隐藏的隐藏,该显示的显示
         */
        stompClient.connect({},
            function(frame) {
            setConnected(true);
            console.log('连接成功Connected: ' + frame);
            document.getElementById("state-info").innerHTML = "***连接成功***";
            // 客户端订阅消息的目的地址:此值WebSocketController中被@SendTo("/topic/getResponse")注解的里配置的值
            stompClient.subscribe('/topic/getResponse', function(respnose){ //2
                showResponse(JSON.parse(respnose.body).responseMessage);
            });

            //订阅queue===接收广播的
            stompClient.subscribe('/topic/sendTopic', function(response) {
                showResponse(JSON.parse(response.body).responseMessage);
                //showResponse((respnose.body).responseMessage);//string返回的处理
            });

            //接收发个单个人的===点对点===????????为什么会收不到
            var mytoken = $('#mytoken').val();
            stompClient.subscribe('/user/queue/sendUser', function(response) {  //  '/user/queue/sendUser'===/'+mytoken+'
                //showResponse(JSON.parse(response.body).responseMessage);
                showResponseBody(response.body);//直接显示response.body

            });


        },
            function errorCallBack (error) {
                // 连接失败时(服务器响应 ERROR 帧)的回调方法
                document.getElementById("state-info").innerHTML = "***连接失败***";
                console.log('连接失败【' + error + '】');
            }


        );//end for connected


    }


    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        document.getElementById("state-info").innerHTML = "***连接未连接***";
        console.log("Disconnected");
    }


    //后台采用@SendTo("/topic/getResponse")注解
    function sendName() {
        var message = $('#message').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        stompClient.send("/receive", {}, JSON.stringify({ 'name': message }));
    }


    //后台采用@SendTo("/topic/getResponse")注解
    function sendName() {
        var message = $('#message').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        stompClient.send("/receive", {}, JSON.stringify({ 'name': message }));
    }

    function sendTopic() {  //发公告===类似发群公告
        var message = $('#message').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        stompClient.send("/sendAllUser", {}, JSON.stringify({ 'name': message }));
    }

    function sendServer() {  //发送到服务端,server不返回数据
        var message = $('#message').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        stompClient.send("/sendServer", {}, JSON.stringify({ 'name': message }));
    }

    function sendAllUser() {  //发公告===类直接发到群中,其实跟公告一样
        var message = $('#message').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        stompClient.send("/sendAllUser", {}, JSON.stringify({ 'name': message }));
    }

    //发送点对点通信---后台对应的采用的是simpMessagingTemplate.convertAndSendToUser
    function sendMyUser() {
        var message = $('#message').val();
        var mytoken = $('#mytoken').val();
        // 客户端消息发送的目的:服务端使用WebSocketController中@MessageMapping("/receive")注解的方法来处理发送过来的消息
        //stompClient.send("/sendMyUser", {}, JSON.stringify({ 'name': message }));

        stompClient.send("/sendMyUser", {}, JSON.stringify({name:mytoken,message:message}));

    }

    //显示消息
    function showResponse(message) {
        var response = $("#response");
        response.html("按ResponseMessage对象返回的responseMessage字段数据:\""+message + "<br\>" + response.html());
    }

    function showResponseBody(response_body) {
        var response = $("#response");
        response.html("按websocket api 返回格式的response.body数据:"+response_body + "<br\>" + response.html());
    }

</script>
</body>
</html>

通过controller转发到html访问websocket


http://localhost:8080//wechatTask/websocket/index

websocket链接情况

添加Socket链接的管理器SocketManager

image.png
public class SocketManager {
    private static final Logger log = LoggerFactory.getLogger(SocketManager.class);
    private static ConcurrentHashMap<String, WebSocketSession> manager = new ConcurrentHashMap<String, WebSocketSession>();

    public static void add(String key, WebSocketSession webSocketSession) {
        log.info("新添加webSocket连接 {} ", key);
        manager.put(key, webSocketSession);
    }

    public static void remove(String key) {
        log.info("移除webSocket连接 {} ", key);
        manager.remove(key);
    }

    public static WebSocketSession get(String key) {
        log.info("获取webSocket连接 {}", key);
        return manager.get(key);
    }

    public static int connectedCount(){
        return manager.size();
    }


}

添加WebSocketDecoratorFactory(用于管理websocket的链接与断开)


/**
 * 服务端和客户端在进行握手时会被执行
 */
@Component
public class WebSocketDecoratorFactory implements WebSocketHandlerDecoratorFactory {
    private static final Logger log = LoggerFactory.getLogger(WebSocketDecoratorFactory.class);
    @Override
    public WebSocketHandler decorate(WebSocketHandler handler) {
        return new WebSocketHandlerDecorator(handler) {
            @Override
            public void afterConnectionEstablished(WebSocketSession session) throws Exception {
                log.info("有人连接啦  sessionId = {}", session.getId()+"链接数量"+SocketManager.connectedCount()+"****连接数一直为0则是校验了taken");

                Principal principal = session.getPrincipal();
                if (principal != null) {
                    log.info("key = {} 存入", principal.getName());
                    // 身份校验成功,缓存socket连接
                    SocketManager.add(principal.getName(), session);
                }


                super.afterConnectionEstablished(session);
            }

            @Override
            public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
                log.info("有人退出连接啦  sessionId = {}", session.getId()+SocketManager.connectedCount());
                Principal principal = session.getPrincipal();
                if (principal != null) {
                    // 身份校验成功,移除socket连接
                    SocketManager.remove(principal.getName());
                }
                super.afterConnectionClosed(session, closeStatus);
            }
        };
    }
}

WebSocketConfig中注入WebSocketDecoratorFactory,并重写configureWebSocketTransport方法


image.png java spring 后台日志 2个浏览器连接后收到不同的信息

关于websocket的文章收录

JMeter测试WebSocket的经验总结
springboot集成websocket需要的都在这里

image.png 效果图

Spring Boot系列十六 WebSocket简介和spring boot集成简单消息代理

image.png

使用spring boot +WebSocket实现(后台主动)消息推送-@ServerEndpoint创立websocket endpoint--实现onOpen、onError、onClose

//socket = new WebSocket("ws://localhost:9094/starManager/websocket/张三");
        var socket;
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else{
            console.log("您的浏览器支持WebSocket");
            
            //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
            //socket = new WebSocket("ws://localhost:9094/starManager/websocket/张三")

            socket = new WebSocket("ws://localhost:9094/starManager/websocket");
            //打开事件
            socket.onopen = function() {
                console.log("Socket 已打开");
                //socket.send("这是来自客户端的消息" + location.href + new Date());
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                console.log(msg.data);
                //发现消息进入    调后台获取
                getCallingList();
            };
            //关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //发生了错误事件
            socket.onerror = function() {
                alert("Socket发生了错误");
            }
             $(window).unload(function(){
                  socket.close();
                });
 
//                                  $("#btnSend").click(function() {
//                                      socket.send("这是来自客户端的消息" + location.href + new Date());
//                                  });
//
//                                  $("#btnClose").click(function() {
//                                      socket.close();
//                                  });
        }

SpringBoot使用WebSocket--SimpMessagingTemplate
声明SimpMessagingTemplate (或者使用@SendTo和@SendToUser注解)
在需要用到推送的地方如Controller,service,Component等地方声明SimpMessagingTemplate
当需要向客户端推送消息时,调用convertAndSend方法,即可推送消息,此处“/topic/send”可随意设置,所有前端订阅该url的客户端都可以收到推送的消息。

messagingTemplate.convertAndSend("/topic/send", result);

springboot+websocket,一篇足够了--管理Socket的类-SocketManager
springboot+websocket,一篇足够了--管理Socket的类-SocketManager -简书
Spring-boot2 WebFlux WebSockit实现-实现心跳
websocket消息推送实现
[java+websocket实现网页聊天室-今日头条](https://www.toutiao.com/a6700358112806699528
spring websocket之sockjs超简单现实
客户端接收服务端消息推送sockjs-client的使用--new SockJS(url, _reserved, options)
利用Spring_Boot WebSocKet实现一个推送的小Demo--全局推送&点对点推动-spring-boot-starter-security
Spring Boot通信之STOMP协议:后台不发送心跳的问题

image.png

spring boot集成WebSocket实时输出日志到web页面--采用阻塞队列
Spring消息之STOMP--留意参数Principal principal
什么是stomp?spring-boot websocket stomp服务构建-@MessageMapping参数详情

相关文章

网友评论

      本文标题:Springboot 技术整合--笔记6--集成websocke

      本文链接:https://www.haomeiwen.com/subject/fruetctx.html