SpringBoot + WebSocket realtime chat

Recently a little bit of time, the last project happened to use websocket broadcast messages, now to sort out some of the previous code, share with you.

WebSocket protocol is a new network protocol based on TCP. It enables full-duplex communication between the browser and the server — allowing the server to actively send messages to the client.

I. Introduction to the environment

Development tool: IntelliJ IDEA

Runtime environment: SpringBoot2.x, ReconnectingWebSocket, JDK1.8+, Maven 3.6 +

ReconnectingWebSocket is a small JavaScript library that encapsulates the WebSocket API and provides an automatic reconnection mechanism when a connection is disconnected.

Simply add:

ws = new WebSocket('ws://.... ');
Copy the code

Replace:

ws = new ReconnectingWebSocket('ws://.... ');
Copy the code

WebSocket property ws. ReadyState:

0 – Indicates that the connection is not established.

1 – Indicates that the connection is established and communication can be performed.

2 – Indicates that the connection is being closed.

3 – Indicates that the connection is closed or cannot be opened.

WebSocket events:

The event Event handler describe
open ws.onopen Triggered when the connection is established
message ws.onmessage Triggered when the client receives data from the server
error ws.onerror Triggered when a communication error occurs
close ws.onclose Triggered when the connection is closed

WebSocket method:

methods describe
Socket.send() Use the connection to send data
Socket.close() Close the connection

Two, code implementation

(1) Create a SpringBoot project

(2) Add POM dependency

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
   <! -- SpringBooot integration with Websocket -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.71</version>
    </dependency>
</dependencies>
Copy the code

(3) Write front-end templatesindex.html


      
<html>
<head>
    <meta charset="utf-8"/>
    <title>SpringBoot-ws</title>
    <script src=".. /js/reconnectingwebsocket.js" type="text/javascript" charset="utf-8"></script>
    <! -- <script src=".. /js/sockjs.min.js" type="text/javascript" charset="utf-8"></script>-->
    <script src=".. /js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
    <link rel="stylesheet" type="text/css" href=".. /css/style.css">
</head>
<body>
<div id="info">
    <div>The sender:<input type="text" id="suer" required="required" placeholder="Please enter sender"></div>
    <div>Receiver:<input type="text" id="ruser" required="required" placeholder="Please enter recipient"></div>
</div>
<div id="index">
</div>
<div class="msg">
    <textarea id="send_content" placeholder="Enter message here..."></textarea>
</div>
<div class="ibtn c">
    <button onclick=openWebsocket()>Open the connection</button>
    <button onclick=closeWebsocket()>Close the connection</button>
    <button onclick=sendMessage()>Send a message</button>
</div>
<script type="text/javascript">
    document.getElementById('send_content').focus();

    var websocket = null;

    / / close the websocket
    function closeWebsocket() {
        //3 indicates closed
        if (3! = websocket.readyState) { websocket.close(); }else {
            alert("Websocket was previously closed."); }}/ / open the websocket
    function openWebsocket() {
        username = $("#suer").val()
        if(username ! ="") {

            // Whether websocket is supported before current browsing
            if ("WebSocket" in window) {
                websocket = new ReconnectingWebSocket("ws://localhost:8080/send/" + username);
                websocket.reconnectInterval = 3000 // Reconnect every 3s, default is every second
            } else if ('MozWebSocket' in window) {
                websocket = new MozWebSocket("ws://localhost:8080/send/" + username);
            } else {
                / / low version
                websocket = new SockJS("http://localhost:8080/sockjs/send/" + username);
            }
        }
        websocket.onopen = function (event) {
            setMessage("Open the connection");
        }

        websocket.onclose = function (event) {
            setMessage("Close connection");
        }

        websocket.onmessage = function (event) {
            // setMessage(event.data);
            setMessageTxt(event.data)

        }

        websocket.onerror = function (event) {
            setMessage("Abnormal connection, reconnection in progress...");
        }

        // Listen for window closing events, when the window is closed, actively close websocket connection, to prevent the connection is not closed, the server will throw exceptions.
        window.onbeforeunload = function () { closeWebsocket(); }}// Displays the message on the web page
    function setMessage(message) {
        alert(message)
    }

    function setMessageTxt(message) {
        mObj = JSON.parse(message)
        var div = document.createElement('div')
        div.innerHTML = "<div class='name l'><h2>" + mObj['from_topic'] + "</h2></div>" +
            "<div class='content w l'>" + mObj['content'] + "</div>"
        div.setAttribute("class"."from_info")
        document.getElementById('index').appendChild(div)
    }

    // Send a message
    function sendMessage() {
        //1 indicates that the connection is ongoing
        if (1 == websocket.readyState) {
            var message = document.getElementById('send_content').value;
            var div = document.createElement('div')
            div.innerHTML = "<div class='name r rcontent'><h2> Me </h2></div>" +
                "<div class='content w r'>" + message + "</div>"
            div.setAttribute("class"."send_info")
            document.getElementById('index').appendChild(div)
            ruser = document.getElementById("ruser").value;
            message = "{'content':'" + message + "','to_topic':'" + ruser + "'}"
            websocket.send(message);
        } else {
            alert("Websocket not connected");
        }
        document.getElementById('send_content').value = "";
        document.getElementById('send_content').focus();
    }
</script>
</body>
</html>
Copy the code

(IV) Server-side code writing

  1. Write SWCrontroller. Java classes

    package com.jhzhong.swchat.controller;
    
    import com.jhzhong.swchat.websocket.WebSocketServer;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    @Controller
    public class SWController {
    
        @Autowired
        private WebSocketServer webSocketServer;
    
        /** * author: [email protected] * Date: 2020-06-24 12:35 AM * desc: Jump to index.html *@return* /
        @RequestMapping("/")
        public String index(a) {
            return "index"; }}Copy the code
  2. Write the WebSocketConfig. Java class to enable WebSocket support.

    package com.jhzhong.swchat.websocket;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    /** * author: [email protected] * Date: 2020-06-24 12:28 AM * desc: Enable WebSocket support */
    @Configuration
    public class WebSocketConfig {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter(a){
            return newServerEndpointExporter(); }}Copy the code
  3. Write the core code class WebSocketServer.java

    package com.jhzhong.swchat.websocket;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import freemarker.log.Logger;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.concurrent.ConcurrentHashMap;
    
    /** * author: [email protected] * Date: 2020-06-24 12:40am * desc: WebSocket server */
    @ServerEndpoint("/send/{topic}")
    @Component
    public class WebSocketServer {
        static Logger logger = Logger.getLogger("WebSocketServer");
        /** * static variable used to record the number of current online connections. It should be designed to be thread-safe. * /
        private static int onlineCount = 0;
        /** * A thread-safe Set for the Concurrent package, used to hold each client's corresponding MyWebSocket object. * /
        private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
        /** * Connects to a client through which to send data to the client */
        private Session session;
        /** * receive channel topic */
        private String topic = "";
    
        /** * Connection established successfully called method */
        @OnOpen
        public void onOpen(Session session, @PathParam("topic") String topic) {
            this.session = session;
            this.topic = topic;
            if (webSocketMap.containsKey(topic)) {
                webSocketMap.remove(topic);
                webSocketMap.put(topic, this);
                // add to set
            } else {
                webSocketMap.put(topic, this);
                // add to set
                addOnlineCount();
                // The number of lines increases by 1
            }
    
            logger.info("User Connection :" + topic + ", current online population is :" + getOnlineCount());
            try {
                sendMessage("Connection successful");
            } catch (IOException e) {
                logger.error("Users." + topic + ", network exception!!!!!!"); }}/** * the connection closes the called method */
        @OnClose
        public void onClose(a) {
            if (webSocketMap.containsKey(topic)) {
                webSocketMap.remove(topic);
                // Delete from set
                subOnlineCount();
            }
            logger.info("User logout :" + topic + ", current online population is :" + getOnlineCount());
        }
    
        /** * The method called after receiving the client message **@paramMessage Indicates the message sent by the client
        @OnMessage
        public void onMessage(String message, Session session) {
            logger.info("Users." + topic + "Information," + message);
            // You can send group messages
            // Save the message to the database, redis
            if (StringUtils.isNotBlank(message)) {
                try {
                    // Parse the sent packet
                    JSONObject jsonObject = JSON.parseObject(message);
                    // Append the sender (to prevent string modification)
                    jsonObject.put("from_topic".this.topic);
                    String to_topic = jsonObject.getString("to_topic");
                    // To the websocket corresponding to the toUserId user
                    if (StringUtils.isNotBlank(to_topic) && webSocketMap.containsKey(to_topic)) {
                        webSocketMap.get(to_topic).sendMessage(jsonObject.toJSONString());
                    } else {
                        logger.error("Requested to_topic:" + to_topic + "Not on this server");
                        // Otherwise not on the server, send to mysql or redis}}catch(Exception e) { e.printStackTrace(); }}}/ * * *@param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            logger.error("User error :" + this.topic + ",原因:" + error.getMessage());
            error.printStackTrace();
        }
    
        /** * implement server active push */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
    
        /** * Send a custom message */
        public static void sendInfo(String message, @PathParam("topic") String topic) throws IOException {
            logger.info("Send a message to :" + topic + ", message :" + message);
            if (StringUtils.isNotBlank(topic) && webSocketMap.containsKey(topic)) {
                webSocketMap.get(topic).sendMessage(message);
            } else {
                logger.error("User" + topic + ", not online!); }}public static synchronized int getOnlineCount(a) {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount(a) {
            WebSocketServer.onlineCount++;
        }
    
        public static synchronized void subOnlineCount(a) { WebSocketServer.onlineCount--; }}Copy the code

3. Run screenshots

  1. Home page screenshots

  2. Transport action figure www.bilibili.com/video/BV1kZ…