Introduction and Use of SpringBoot-WebSocket-STMOP

brief introduction

  • WebSocket: It is a network communication protocol. Servers can actively push information to clients, and clients can also actively send information to servers. details
  • sockjs-client: js library, which can simulate support for WebSocket if the browser does not support WebSocket github
  • STOMP: Simple (Streaming) Text Oriented Message Protocol introduce
  • stomp-websocket: js library, which provides a WebSocket based on STOMP client gihub

CODE

The code is available in demo, but some are not simplified for blog effect.

Maven Increases Dependence

    <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>

SpringBoot configuration

Matters needing attention

  • Rewrite DefaultHandshakeHandler's determineUser method to generate user channel names by itself, which can be ignored if spring Security is used
  • enableSimpleBroker: Set the prefix of the message received by the client
  • SetUser Destination Prefix: Specifies the prefix of the user channel, which must be set in enableSimpleBroker
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //Set the endpoint and connect the front end through / ContextPath / endpoint
        stompEndpointRegistry.addEndpoint("/any-socket").addInterceptors(new HandshakeInterceptor() {

            /**
             * Intercept before shaking hands, store user information to attributes, and use subsequent user channels
             * @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() {
            /**
             * Specify the handshake body generation rule, which will be used when subsequent user messages are received. The default user channel is / User Destination Prefix /{Principal. getName}/ channel.
             * @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"));
            }
        //Support SockJS
        }).withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {

        messageBrokerRegistry.setApplicationDestinationPrefixes("/app");
// The prefix information of the client subscription address, that is, the prefix information of the client receiving the message sent by the server
 messageBrokerRegistry.enableSimpleBroker("/queue", "/topic");
        //Specify user channel prefix, which is modifiable by default 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();
                        //On-line related operations
                        super.afterConnectionEstablished(session);
                    }

                    @Override
                    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
                            throws Exception {
                        String username = session.getPrincipal().getName();
                      //Offline related operations
                        super.afterConnectionClosed(session, closeStatus);
                    }
                };
            }
        });
    }
}

Server-side code

* * statement * *

  • @ Message Mapping ("/ sendMsg") corresponds to the path invoked when the front end sends the message, and the access path is / Application Destination Prefixes / sendMsg, which has nothing to do with Content Path at this time.
  • ConversAndSendToUser: Send a message to the specified user, corresponding to the determineUser in the settings, and the specified user channel prefix, the final route to send: / User channel prefix / Principal.getName / suffix
  • ConvAndSend: Send a message to a specified channel, using @SendTo instead
  • @ SendToUser: Sends a message to the user channel corresponding to the requested user, which is not interchangeable with convertAndSendToUser
    @MessageMapping("/sendMsg")
    public void sendMsg(Principal principal, String message) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy year MM month dd day 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));
        }
    }

Front-end code

    function connect() {
        //Connect endpoints, and add project paths
        var socket = new SockJS(_baseUrl+'any-socket');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            //Listen for / topic/notice messages on this channel
            stompClient.subscribe('/topic/notice', function (message) {
                showMessage(JSON.parse(message.body));
            });
            //Listen for the current logged-in user's message on this channel, corresponding to the server 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
            };;
            //Send a message to the corresponding path of MessageMapping
            stompClient.send("/app/sendMsg", {}, JSON.stringify(msg));
            $("#message").val("");
        });

Demo

demo refers to this Blog Remove the Spring Security section, modify the one-to-one message sending rules, write more rudimentary, when the first user logs in, you will see the error. After the second user logs in, refreshing the page of the first logged-in user loads the user and allows peer-to-peer chat.
https://gitee.com/MeiJM/stompDemo

Reference material

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



From Wiz

Keywords: Session Spring socket JSON

Added by comcentury on Thu, 16 May 2019 09:43:30 +0300