SpringBoot-WebSocket-STMOP简介与使用

简介:

简介

  • WebSocket:是一种网络通信协议,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息详情
  • sockjs-client:js库,如果浏览器不支持 WebSocket,该库可以模拟对 WebSocket 的支持github
  • STOMP:简单(流)文本定向消息协议 介绍
  • stomp-websocket:js库,提供一个基于STOMP客户端的WebSocket gihub

CODE

已下代码在demo中都有,但是有的为了博客效果没有简化。

Maven 增加依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-websocket</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
AI 代码解读

SpringBoot 配置

注意事项

  • 重写DefaultHandshakeHandler的determineUser方法来自己实现生成用户频道名称,如使用的是spring Security则可忽略此条
  • enableSimpleBroker:设置客户端接收消息的前缀
  • setUserDestinationPrefix:指定用户频道的前缀,这个前缀必须在enableSimpleBroker中设置过
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //设置端点,前端通过 /ContextPath/端点 进行连接
        stompEndpointRegistry.addEndpoint("/any-socket").addInterceptors(new HandshakeInterceptor() {

            /**
             * 握手前拦截,往attributes存储用户信息,后续用户频道使用
             * @param request
             * @param response
             * @param wsHandler
             * @param attributes
             * @return
             * @throws Exception
             */
            @Override
            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
                boolean result = false;
                HttpSession session =getSession(request);
                if (session != null){
                    User user =(User)getSession(request).getAttribute("user");
                    if(user != null){
                        attributes.put("user",user);
                        result = true;
                    }
                }
                return result;
            }
            @Nullable
            private HttpSession getSession(ServerHttpRequest request) {
                if (request instanceof ServletServerHttpRequest) {
                    ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
                    return serverRequest.getServletRequest().getSession();
                }
                return null;
            }
            @Override
            public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {}
        }).setHandshakeHandler(new DefaultHandshakeHandler() {
            /**
             * 指定握手主体生成规则,后续接收用户消息时会使用,默认用户频道为/UserDestinationPrefix/{Principal.getName}/频道
             * @param request
             * @param wsHandler
             * @param attributes
             * @return
             */
            @Nullable
            @Override
            protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
                return new UserPrincipal((User) attributes.get("user"));
            }
        //支持SockJS
        }).withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {

        messageBrokerRegistry.setApplicationDestinationPrefixes("/app");
// 客户端订阅地址的前缀信息,也就是客户端接收服务端发送消息的前缀信息
 messageBrokerRegistry.enableSimpleBroker("/queue", "/topic");
        //指定用户频道前缀,默认为user可修改
        messageBrokerRegistry.setUserDestinationPrefix("/queue");
    }

    @Override
    public void configureWebSocketTransport(final WebSocketTransportRegistration registration) {
        registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
            @Override
            public WebSocketHandler decorate(final WebSocketHandler handler) {
                return new WebSocketHandlerDecorator(handler) {
                    @Override
                    public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
                        String username = session.getPrincipal().getName();
                        //上线相关操作
                        super.afterConnectionEstablished(session);
                    }

                    @Override
                    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
                            throws Exception {
                        String username = session.getPrincipal().getName();
                      //离线相关操作
                        super.afterConnectionClosed(session, closeStatus);
                    }
                };
            }
        });
    }
}
AI 代码解读

服务端代码

** 说明 **

  • @MessageMapping("/sendMsg"),对应前端发送消息时调用的路径,访问路径为/ApplicationDestinationPrefixes/sendMsg,此时已与ContextPath无关。
  • convertAndSendToUser:向指定用户发送消息,对应设置中的determineUser,和指定的用户频道前缀,最终发送的路径:/用户频道前缀/Principal.getName/后缀
  • convertAndSend:向指定频道发送消息,可以使用@SendTo代替
  • @SendToUser:向请求的用户对应的用户频道发送消息,与convertAndSendToUser不能互换
    @MessageMapping("/sendMsg")
    public void sendMsg(Principal principal, String message) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Message msg = JsonUtils.toObject(message, Message.class);
        try {
            msg.setSendTime(sdf.format(new Date()));
        } catch (Exception e) {
        }
        if (!"TO_ALL".equals(msg.getReceiver())) {
            template.convertAndSendToUser(msg.getReceiver(), "/chat", JsonUtils.toJson(msg));
        } else {
            template.convertAndSend("/notice", JsonUtils.toJson(msg));
        }
    }
AI 代码解读

前端代码

    function connect() {
        //连接端点,此时需加上项目路径
        var socket = new SockJS(_baseUrl+'any-socket');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            //监听/topic/notice这个频道的消息
            stompClient.subscribe('/topic/notice', function (message) {
                showMessage(JSON.parse(message.body));
            });
            //监听当前登录用户这个频道的消息,对应服务端convertAndSendToUser
            stompClient.subscribe("/queue/"+_username+"/chat", function (message) {
                showMessage(JSON.parse(message.body));
            });
        });
    }

        $("#send").click(function () {
            debugger;
            var msg = {
                "username":_username
                ,"avatar":_avatar
                ,"content":$("#message").val()
                ,"receiver":target
            };;
            //向MessageMapping对应路径发送消息
            stompClient.send("/app/sendMsg", {}, JSON.stringify(msg));
            $("#message").val("");
        });
AI 代码解读

Demo

demo参考了这个博客,去掉了Spring Security的部分,修改了一对一消息发送的规则,写的比较简陋,第一个用户登录的时候会报错大家将就看看就行。第二个用户登录后刷新第一个登陆用户的页面会加载用户,就可以点对点的聊天了。 
https://gitee.com/MeiJM/stompDemo

参考资料

https://www.jianshu.com/p/4ef5004a1c81 
https://www.jianshu.com/p/0f498adb3820 
https://spring.io/guides/gs/messaging-stomp-websocket/ 
https://www.callicoder.com/spring-boot-websocket-chat-example/ 
http://www.ruanyifeng.com/blog/2017/05/websocket.html 
https://github.com/spring-guides/gs-messaging-stomp-websocket



目录
打赏
0
0
0
0
891
分享
相关文章
WebSocket协议相关的测试命令工具使用简介
本文介绍了针对WebSocket的测试工具wscat和websocat的基本使用方法,以及通过curl命令测试HTTP/HTTPS协议的方式。对于WebSocket,直接使用curl测试较为复杂,推荐使用wscat或websocat。文中详细说明了这两种工具的安装步骤、常用参数及连接示例,例如在ECS上开启8080端口监听并进行消息收发测试。此外,还提供了curl命令的手动设置头部信息以模拟WebSocket握手的示例,但指出curl仅能作为客户端测试工具,无法模拟服务器。
218 4
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
375 1
|
2月前
|
SpringBoot快速搭建WebSocket服务端和客户端
由于工作需要,研究了SpringBoot搭建WebSocket双向通信的过程,其他的教程看了许多,感觉讲得太复杂,很容易弄乱,这里我只展示快速搭建过程。
213 1
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
2012 1
springBoot集成websocket实时消息推送
本文介绍了如何在Spring Boot项目中集成WebSocket实现实时消息推送。首先,通过引入`spring-boot-starter-websocket`依赖,配置`WebSocketConfig`类来启用WebSocket支持。接着,创建`WebSocketTest`服务器类,处理连接、消息收发及错误等事件,并使用`ConcurrentHashMap`管理用户连接。最后,前端通过JavaScript建立WebSocket连接,监听消息并进行相应处理。此方案适用于需要实时通信的应用场景,如聊天室、通知系统等。
288 2
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问