Small demo of WebSocket chat function

 1. Introduction to WebSocket

1.1 What is WebSocket?

The WebSocket protocol is a network protocol based on TCP, which realizes the full-duplex (Full-duplex) communication between the browser and the server. It allows the server to actively push data to the client, which makes the data exchange between the client and the server easier and more efficient. In the WebSocket API, the browser and the server only need to complete a handshake, and a persistent connection can be established between the two, and two-way data transmission can be performed. After the handshake, WebSocket directly communicates messages based on TCP. It is just a very light encapsulation based on TCP. It just converts TCP byte streams into message streams (text or binary). As for how to parse the content of these messages Completely dependent on the application itself.

1.2 Why do you need WebSocket?

We know that the HTTP protocol has a flaw: communication can only be initiated by the client, and the server cannot push data to a client. However, in some scenarios, data push is a very necessary function. In order to realize the push technology, the technology used is polling, that is: the client sends a message to the browser at a specific time interval (such as every 1 second). The server sends out an HTTP request, and then the server returns the latest data to the client's browser.

For example, in a takeaway scenario, when the rider's position is updated, the server pushes the rider's position data to the client. If the HTTP protocol is used, then only polling is possible. The polling mode has obvious disadvantages, that is, the browser needs to continuously send requests to the server. However, the HTTP request may contain a long header, and the real valid data may be only a small part. Obviously, this will waste a lot of bandwidth. And other resources, similarly, the data timeliness is low, and there is a certain data delay. In this case, WebSocket appeared. Using the WebSocket protocol, the server can actively push messages to the client, and at the same time, the client can send messages to the server. This can better save server resources and bandwidth; and enable more real-time communication. With the popularity of HTML 5, WebSocket has become an international standard, and all current mainstream browsers already support it.

1.3 Advantages of WebSocket

  • Less control overhead . After the connection is established, when data is exchanged between the server and the client, the header of the data packet used for protocol control is relatively small. In the absence of extensions, for server-to-client content, the size of this header is only 2 to 10 bytes (related to the length of the packet); for client-to-server content, this header also needs to add an additional 4-byte mask. Compared with the HTTP protocol that carries complete header information every time, this overhead is significantly reduced.

  • Stronger real-time performance . Since the WebSocket protocol is full-duplex, the server can actively push data to the client at any time. Compared with HTTP requests, which must wait for the client to initiate a request for the server to respond, the delay is significantly less; even compared with Comet and other similar long polling, WebSocket can transfer data more efficiently in a short time.

  • Stay connected . Different from HTTP, Websocket needs to create a connection first, which makes it a stateful protocol, and some state information can be omitted during communication later, while HTTP requests need to carry state information (such as Token, etc.) in each request .

  • Better binary support . Websocket defines binary frames, which can handle binary data more easily than HTTP. Websocket defines extensions, and users can extend the protocol and implement some custom sub-protocols. For example, some browsers support Gzip compression, etc.

  • Better compression . Compared with HTTP compression, with appropriate extension support, Websocket can inherit the context of previous content, and can significantly improve the compression rate when transferring similar data.

2. WebSocket events

We know that the HTTP protocol uses the uniform resource identifiers of http and https. WebSocket is similar to HTTP, using ws or wss (similar to HTTPS), where wss means Websocket over TLS. For example:

ws://aiqinhai.com/wsapi
wss://secure.aiqinhai.com/

WebSocket uses the same TCP port as HTTP and can bypass most firewall restrictions. By default, the WebSocket protocol uses port 80; when running over TLS, port 443 is used by default. WebSocket is just a very light layer of encapsulation based on the Socket protocol. Several basic events such as open, close, error, and message are defined in the WebSocket API, which makes the use of WebSocket very simple. The following events are defined in the WebSocket API:

event

event handler

describe

open

Sock unopened

Triggered when a connection is established

message

Sokcket onmessage

Triggered when the client receives data from the server

error

Sock on error

Triggered when a communication error occurs

close

Sokcket onclose

Fired when the connection is closed

3. Spring Boot integrates WebSocket to realize chat room

Spring Boot provides the Websocket component spring-boot-starter-websocket to support the use of Websocket in the Spring Boot environment. Let's take a multi-person online chat room as an example to demonstrate how Spring Boot integrates Websocket to push messages on the server side.

3.1 Create the front-end page

First, create a spring boot project: spring-boot-starter-websocket. Next, we build the front-end interactive page, create an index.html page and implement WebSocket communication in js. The complete page code is as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Chat Room</title>
    <script type="text/javascript">
        var urlPrefix ='ws://localhost:8080/chat/';
        var ws = null;
        // 加入
        function join() {
            var username = document.getElementById('uid').value;
            var url = urlPrefix + username;
            ws = new WebSocket(url);
            ws.onmessage = function(event){
                var ta = document.getElementById('responseText');
                ta.value += event.data+"\r\n";
            };
            ws.onopen = function(event){
                var ta = document.getElementById('responseText');
                ta.value += "建立 websocket 连接... \r\n";
            };
            ws.onclose = function(event){
                var ta = document.getElementById('responseText');
                ta.value += "用户["+username+"] 已经离开聊天室! \r\n";
                ta.value += "关闭 websocket 连接. \r\n";
            };
        }

        // 退出
        function exit(){
            if(ws) {
                ws.close();
            }
        }

        // 发送消息
        function send(){
            var message = document.getElementById('message').value;
            if(!window.WebSocket){return;}
            if(ws.readyState == WebSocket.OPEN){
                ws.send(message);
            }else{
                alert("WebSocket 连接没有建立成功!");
            }
        }
    </script>
</head>
<body>
<form onSubmit="return false;">
    <h3>爱琴孩聊天室</h3>
    <textarea id="responseText" style="width: 1024px;height: 300px;"></textarea>
    <br/>
    <br />
    <label>昵称 : </label><input type="text" id="uid" /> &nbsp;
    <input type="button" value="加入聊天室" onClick="join()" /> &nbsp;

    <input type="button" value="离开聊天室" onClick="exit()" />
    <br />
    <br />
    <label>消息 : </label><input type="text" id="message" /> &nbsp; <input type="button" value="发送消息" onClick="send()" />
</form>
</body>
</html>

In the above example, the code related to WebSocket communication is defined in js, such as: ws.onopen, ws.onmessage, ws.onclose and other events.

3.2 Create a backend service

Next, we started to create the background WebSocket service to realize the WebSocket background communication service.

step 1: Introduce related dependencies

First, modify the pom.xml file of the project, mainly adding Web and Websocket components. The specific code is as follows:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

step2: message reception

First create the ChatServerEndpoint class, and use the @ServerEndpoint annotation to create a WebSocket EndPoint to implement client connection, message reception, and other events. The specific sample code is as follows:

@Component
@ServerEndpoint("/chat/{username}")
public class ChatServerEndpoint {
    private static final Logger logger = LoggerFactory.getLogger(ChatServerEndpoint.class);

    @OnOpen
    public void openSession(@PathParam("username") String username, Session session) {
        WebSocketUtils.ONLINE_USER_SESSIONS.put(username, session);
        String message = "欢迎用户[" + username + "] 来到聊天室!";
        logger.info("用户登录:" + message);
        WebSocketUtils.sendMessageAll(message);
    }

    @OnMessage
    public void onMessage(String message,@PathParam("username") String username) {
        logger.info("发送消息:" + message);
        WebSocketUtils.sendMessageAll("用户[" + username + "] : " + message);
    }

    @OnClose
    public void onClose(Session session,  @PathParam("username") String username) {
        //当前的Session 移除
        WebSocketUtils.ONLINE_USER_SESSIONS.remove(username);
        //并且通知其他人当前用户已经离开聊天室了
        WebSocketUtils.sendMessageAll("用户[" + username + "] 已经离开聊天室了!");
        try {
            session.close();
        } catch (IOException e) {
            logger.error("onClose error", e);
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            session.close();
        } catch (IOException e) {
            logger.error("onError excepiton", e);
        }
        logger.info("Throwable msg " + throwable.getMessage());
    }
}

In the above example, we use the @ServerEndpoint("/chat/{username}") annotation to listen to the WebSocket information of this address, and the client also receives and sends messages to the server through this address. At the same time, use @OnOpenannotations to implement client connection events, @OnMessageannotations to implement message sending events, @OnCloseannotations to implement client connection close events, and @OnErrorannotations to implement message error events.

step3: message sending

We first create a WebSocketUtils tool class, which is used to store the online user information of the chat room and the function of sending messages to the client. The specific code is as follows:​​​​​​​​


public final class WebSocketUtils {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketUtils.class);

    // 存储 websocket session
    public static final Map<String, Session> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>();

    /**
     * @param session 用户 session
     * @param message 发送内容
     */
    public static void sendMessage(Session session, String message) {
        if (session == null) {
            return;
        }
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null) {
            return;
        }
        try {
            basic.sendText(message);
        } catch (IOException e) {
            logger.error("sendMessage IOException ",e);
        }
    }

    /**
     * 推送消息到其他客户端
     * @param message
     */
    public static void sendMessageAll(String message) {
        ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, message));
    }
}

step4: Enable the WebSocket function

To modify the project startup class, you need to add @EnableWebSocket to enable the WebSocket function. The specific sample code is as follows:​​​​​​​​

@EnableWebSocket
@SpringBootApplication
public class SpringbootWebsocketDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebsocketDemoApplication.class, args);
    }
}

Step5: Inject ServerEndpointExporter. Because the ServerEndpointExporter class file was misplaced before, ServerEndpointExporter was not injected into the Spring container, and the front-end JS made a new WebSocket (url) connection, which always reported an error and failed to connect.

 Later, I realized that the location of the ServerEndpointExporter class was misplaced. When using @ComponentScan()annotations in SpringBoot for component scanning and loading classes, the default scanning scope[ProjectName]Application is the subpackage of the package (direct parent package) where the startup class ( ) is located. That is to say, the classes under the package to be scanned should be located under the path where the startup class is located.

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

Above, our WebSocket server content is completed. Next, we verify that the entire chat room is functioning normally?

3.3 Verification test

Earlier, we have introduced the front and back functions of the entire WebSocket chat room. Next, we verify that the entire chat room is functioning normally? First, start the project and enter the address in the browser: http://localhost:8080/ to open three chat room pages. As shown below:

 Then, on the two chat room pages, enter two nicknames and join the chat room, successfully establish a WebSocket connection with the server, and then send messages in the chat room.

demo source code

Guess you like

Origin blog.csdn.net/qq_28165595/article/details/130627902