Several implementation methods of using WebSocket in Spring Boot - detailed explanation

brief introduction

The so-called WebSocket is similar to Socket. Its function is to enable the client and server in Web applications to establish full duplex communication. There are three ways to use WebSocket in Spring based applications:

  1. Implemented using @ ServerEndpoint annotation provided by Java
  2. It is implemented using the low-level WebSocket API provided by Spring
  3. Implemented using STOMP messages

Next, I will give a brief introduction to these three implementation methods. In addition, for more information about the properties of WebSocket, please refer to the following article: WebSocket Quest

Note: for the complete source code of this article, please refer to: https://github.com/zifangsky/WebSocketDemo

Implemented using @ ServerEndpoint annotation provided by Java

(1) Listen for a WebSocket request path using the @ ServerEndpoint annotation:

Here, the connection port / reverse of the client is monitored, and how to handle messages sent by the client is defined
 

package cn.zifangsky.samplewebsocket.websocket;



import javax.websocket.OnMessage;

import javax.websocket.Session;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;



/**

* ReverseWebSocketEndpoint

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@ServerEndpoint("/reverse")

public class ReverseWebSocketEndpoint {



    @OnMessage

    public void handleMessage(Session session, String message) throws IOException {

        session.getBasicRemote().sendText("Reversed: " + new StringBuilder(message).reverse());

    }



}

(2) WebSocket related configuration:
 

package cn.zifangsky.samplewebsocket.config;



import cn.zifangsky.samplewebsocket.websocket.ReverseWebSocketEndpoint;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.config.annotation.EnableWebSocket;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;



/**

* WebSocket Related configuration

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@Configuration

@EnableWebSocket

public class WebSocketConfig{



    @Bean

    public ReverseWebSocketEndpoint reverseWebSocketEndpoint() {

        return new ReverseWebSocketEndpoint();

    }



    @Bean

    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();

    }



}

(3) Sample page:

slightly

It is implemented using the low-level WebSocket API provided by Spring

Spring 4.0 supports WebSocket communication, including:

  • Low level API for sending and receiving messages;

  • High level API for sending and receiving messages;

  • A template for sending messages;

  • It supports SockJS to solve the problem that the browser, server and agent do not support WebSocket.

Using the low-level API provided by Spring to implement WebSocket mainly requires the following steps:

(1) Add a WebSocketHandler:

Define a message processing class that inherits the AbstractWebSocketHandler class, and then customize the processing of "establish connection", "receive / send message", "exception", etc

package cn.zifangsky.samplewebsocket.websocket;



import cn.zifangsky.samplewebsocket.service.EchoService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.socket.CloseStatus;

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketSession;

import org.springframework.web.socket.handler.TextWebSocketHandler;



import javax.annotation.Resource;

import java.text.MessageFormat;



/**

* Example by inheriting {@ link org.springframework.web.socket.handler.AbstractWebSocketHandler}

*

* @author zifangsky

* @date 2018/10/9

* @since 1.0.0

*/

public class EchoWebSocketHandler extends TextWebSocketHandler{

    private final Logger logger = LoggerFactory.getLogger(getClass());



    @Resource(name = "echoServiceImpl")

    private EchoService echoService;



    @Override

    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        logger.debug("Opened new session in instance " + this);

    }



    @Override

    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        //Echo information returned by assembly

        String echoMessage = this.echoService.echo(message.getPayload());

        logger.debug(MessageFormat.format("Echo message \"{0}\"", echoMessage));



        session.sendMessage(new TextMessage(echoMessage));

    }



    @Override

    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

        session.close(CloseStatus.SERVER_ERROR);

        logger.debug("Info: WebSocket connection closed.");

    }

}

(2) WebSocket related configuration:

package cn.zifangsky.samplewebsocket.config;

import cn.zifangsky.samplewebsocket.websocket.EchoWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * WebSocket Related configuration
 *
 * @author zifangsky
 * @date 2018/9/30
 * @since 1.0.0
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(echoWebSocketHandler(), "/echoMessage");
        registry.addHandler(echoWebSocketHandler(), "/echoMessage_SockJS").withSockJS();
    }

    /**
     * Example by inheriting {@ link org.springframework.web.socket.handler.AbstractWebSocketHandler}
     */
    @Bean
    public WebSocketHandler echoWebSocketHandler(){
        return new EchoWebSocketHandler();
    }

}


(3) Two sample pages: omitted

As can be seen from the above code, in addition to configuring the basic WebSocket (that is, the connection address of / echoMessage), SockJS is also used to configure the alternative scheme when the browser does not support WebSocket Technology (that is, the connection address of / echoMessage_SockJS).

Implemented using STOMP messages

The so-called STOMP(Simple Text Oriented Messaging Protocol), A frame based wire format layer is provided on the basis of WebSocket. It defines a set of standard formats for sending simple Text messages (stop messages are based on Text, and of course they also support the transmission of binary data). At present, many server message queues already support stop, such as RabbitMQ, ActiveMQ, etc.

(1) WebSocket related configuration:

package cn.zifangsky.stompwebsocket.config;

import cn.zifangsky.stompwebsocket.interceptor.websocket.MyChannelInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
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;

/**
 * WebSocket Related configuration
 *
 * @author zifangsky
 * @date 2018/9/30
 * @since 1.0.0
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{
    @Autowired
    private MyChannelInterceptor myChannelInterceptor;

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp-websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //The client needs to send the message to the / message/xxx address
        registry.setApplicationDestinationPrefixes("/message");
        //The server broadcasts the path prefix of the message, and the client needs to subscribe to the message at / topic/yyy
        registry.enableSimpleBroker("/topic");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(myChannelInterceptor);
    }

}


As can be seen from the above code, several addresses are set here, which are briefly explained as follows:

  • First, an endpoint named / STOMP websocket is registered, that is, the address of the STOMP client connection.

  • In addition, it is defined that the prefix of WebSocket messages processed by the server is / message, which is used for the client to send messages to the server (for example, if the client sends messages to the address of / message/hello, the server receives and processes messages through the annotation @ MessageMapping("/ hello")

  • Finally, a simple message proxy is defined, that is, the path prefix of the server broadcast message (for example, if the client listens to the address / topic / meeting, the server can send a stop message to the client through the annotation @ SendTo("/ topic / meeting").

It should be noted that the above code also adds an interceptor named MyChannelInterceptor to print the log after the client disconnects. Relevant codes are as follows:

package cn.zifangsky.stompwebsocket.interceptor.websocket;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.stereotype.Component;

import java.security.Principal;
import java.text.MessageFormat;

/**
 * Customize {@ link org.springframework.messaging.support.ChannelInterceptor} to handle disconnection
 *
 * @author zifangsky
 * @date 2018/10/10
 * @since 1.0.0
 */
@Component
public class MyChannelInterceptor implements ChannelInterceptor{
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        StompCommand command = accessor.getCommand();

        //The user has disconnected
        if(StompCommand.DISCONNECT.equals(command)){
            String user = "";
            Principal principal = accessor.getUser();
            if(principal != null && StringUtils.isNoneBlank(principal.getName())){
                user = principal.getName();
            }else{
                user = accessor.getSessionId();
            }

            logger.debug(MessageFormat.format("user{0}of WebSocket The connection has been disconnected", user));
        }
    }

}


(2) Process messages using @ MessageMapping and @ SendTo annotations:

@The MessageMapping annotation is used to listen to the client message of the specified path, while the @ SendTo annotation is used to send the server message to the client listening to the path.

package cn.zifangsky.stompwebsocket.controller;

import cn.zifangsky.stompwebsocket.model.websocket.Greeting;
import cn.zifangsky.stompwebsocket.model.websocket.HelloMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

/**
 * Greeting
 * @author zifangsky
 * @date 2018/9/30
 * @since 1.0.0
 */
@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greeting")
    public HelloMessage greeting(Greeting greeting) {
        return new HelloMessage("Hello," + greeting.getName() + "!");
    }
}


(3) Example page: omitted

Keywords: Java Spring Spring Boot websocket

Added by rami on Tue, 14 Dec 2021 21:52:07 +0200