SpringBoot series - integrated WebSocket real-time communication

WebSocket is a network technology for full duplex communication between browser and server provided by HTML5. WebSocket communication protocol was set as standard RFC 6455 by IETF in 2011, and WebSocket API was set as standard by W3C. In the WebSocket API, the browser and server only need to shake hands, and then a fast channel is formed between the browser and the server. The two can directly transmit data to each other.

Note features:

  • It provides the function of duplex asynchronous communication for the browser and server, that is, the server can actively push information to the client, and the client can also actively send information to the server. It is a real two-way equal dialogue and belongs to a kind of server push technology.
  • Based on TCP protocol, the server-side implementation is relatively easy.
  • It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the handshake phase adopts HTTP protocol, so it is not easy to shield the handshake and can pass through various HTTP proxy servers.
  • The data format is light, the performance overhead is small, and the communication is efficient.
  • You can send text or binary data.
  • There is no homology restriction, and the client can communicate with any server.
  • The protocol identifier is ws (wss if encrypted), and the server URL is the URL.

There are many ways to implement the WebSocket protocol, such as the STOMP based implementation officially recommended by SpringBoot, which is very simple to implement, and through socket Io. Here I'll talk about the implementation scheme based on STOMP.

summary

STOMP: simple text oriented messaging protocol, which is a simple text message transmission protocol and a sub protocol of WebSocket. It provides an interoperable connection format and allows STOMP clients to interact with any STOMP message Broker. STOMP protocol is widely used in many languages and platforms because of its simple design and easy to develop client.

Introduce dependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

I like to use jetty instead of tomcat, so I write this. You can just introduce spring boot starter websocket.

Configuration class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/simple")
                .setAllowedOrigins("*") //Solve cross domain problems
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
    }
}

The description of this class is as follows:

  1. @The enable websocketmessage broker annotation indicates that the STOMP protocol is enabled to transmit agent-based messages. Broker means agent.
  2. The registerstampendpoints method represents the node that registers the STOMP protocol and specifies the URL of the mapping
  3. addEndpoint(“/simple”). withSockJS(); It is used to register the STOMP protocol node and specify the use of SockJS
  4. The configureMessageBroker method is used to configure the message broker. Since we implement the push function, the message broker here is / topic

Request message class

1
2
3
4
5
6
7
public class RequestMessage {
    private String name;

    public String getName() {
        return name;
    }
}

Response message class

1
2
3
4
5
6
7
8
9
10
11
public class ResponseMessage {
    private String responseMessage;

    public ResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }

    public String getResponseMessage() {
        return responseMessage;
    }
}

Spring MVC controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Controller
public class WsController {

    private final SimpMessagingTemplate messagingTemplate;

    @Autowired
    public WsController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageMapping("/welcome")
    @SendTo("/topic/say")
    public ResponseMessage say(RequestMessage message) {
        System.out.println(message.getName());
        return new ResponseMessage("welcome," + message.getName() + " !");
    }

    /**
     * Timed push message
     */
    @Scheduled(fixedRate = 1000)
    public void callback() {
        // Discovery message
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        messagingTemplate.convertAndSend("/topic/callback", "Timing push message time: " + df.format(new Date()));
    }
}

The @ MessageMapping annotation added on the say method is similar to the @ RequestMapping we used earlier@ The SendTo annotation indicates that when the server has a message to push, it will send a message to the browser subscribed to the path in @ SendTo.

In addition, I have also defined a method of regularly pushing messages. This method will actively push messages to clients subscribed to the topic / topic/callback every 1 second.

So far, the server side has been written. You can see that the writing of the server is very simple.

client

Here, I use the browser's js to test. First, I add three js files:

  1. Client script for STOMP protocol STOMP js
  2. SockJS client script sock js
  3. Page dom operation script jQuery

These three js files can be found in the source code of my github project.

The following is the demo page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Broadcast type WebSocket</title>
    <script src="js/sockjs.min.js"></script>
    <script src="js/stomp.js"></script>
    <script src="js/jquery-3.1.1.js"></script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #e80b0a; "> sorry, the browser does not support websocket < / H2 > < / noscript >
<div>
    <div>
        <button id="connect" onclick="connect();">connect</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>

    <div id="conversationDiv">
        <label>Enter your name</label><input type="text" id="name"/>
        <button id="sendName" onclick="sendName();">send out</button>
        <p id="response"></p>
        <p id="callback"></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();
        $("#callback").html();
    }

    function connect() {
        var socket = new SockJS('http://localhost:8092/simple');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            setConnected(true);
            console.log('Connected:' + frame);
            stompClient.subscribe('/topic/say', function (response) {
                showResponse(JSON.parse(response.body).responseMessage);
            });
            // In addition, register for scheduled task acceptance
            stompClient.subscribe('/topic/callback', function (response) {
                showCallback(response.body);
            });
        });
    }

    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log('Disconnected');
    }

    function sendName() {
        var name = $('#name').val();
        console.log('name:' + name);
        stompClient.send("/welcome", {}, JSON.stringify({'name': name}));
    }

    function showResponse(message) {
        $("#response").html(message);
    }
    function showCallback(message) {
        $("#callback").html(message);
    }
</script>
</body>
</html>

Click the "connect" button on the page to start connecting to the / simple node. After entering the name and clicking send, a message will be sent to the url of / welcome.

If you subscribe to two topics at the same time: / topic/say # and / topic/callback, you will receive the return of the server's say method and regular push messages.

Demonstration effect:

Every second, the page will refresh to display the current time. At the same time, the console will also print messages pushed from time to time.

Added by [e]r!k on Tue, 08 Feb 2022 02:17:05 +0200