SpringBoot+WebSocket+Chat


One, WebSocket introduction

Instant messaging on websites is very common, such as QQ on web pages, chat systems, etc. According to previous technical capabilities, polling and Comet technologies are usually used to solve the problem.
The HTTP protocol is a non-persistent, one-way network protocol. After the connection is established, the browser is only allowed to send a request to the server before the server can return the corresponding data. When instant communication is required, the browser sends a Request request to the server at a specific time interval (such as 1 second) through polling, and then returns the latest data to the browser. The most obvious disadvantage of this method is that it needs to send requests continuously, and usually the header of HTTP request is very long. In order to transmit a small amount of data, it needs to pay a huge price, which is very uneconomical and takes up a lot of bandwidth.
Disadvantages: It will lead to too many unnecessary requests, waste of traffic and server resources, every request and response will waste a certain amount of traffic on the same header information.
However, the emergence of WebSocket can make up for this shortcoming. In WebSocket, only the server and the browser need to perform a handshake action through the HTTP protocol, and then a separate TCP communication channel is established for data transmission.

Two, WebSocket operating mechanism

WebSocket is a new protocol of HTML5 and a typical application layer protocol. It implements full-duplex communication between the browser and the server, which can better save server resources and bandwidth and achieve real-time communication. It is built on TCP and transmits data through TCP like HTTP, but the biggest difference between it and HTTP is:
WebSocket is a two-way communication protocol. After a connection is established, both the WebSocket server and Browser/Client Agent can actively send or receive data to each other, just like Socket;
WebSocket requires a TCP-like client and server to connect through handshake. Can communicate with each other after successful connection.
The interaction between the traditional HTTP client and server in non-WebSocket mode is shown in the

following figure: the interaction between the client and server in WebSocket mode is as follows:
Insert picture description here

Three, WebSocket implementation

Here is a simple chat routine to demonstrate the spring boot + websocket code.
Introduce maven

 <!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

Configuration class WebConfig

package org.antry.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @ClassName WebSocketConfig
 * @Description TODO
 * @Autor TT
 * @Date 2020/10/9 23:00
 * @Version 1.0
 */
@Configuration
public class WebSocketConfig {
    
    

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

}

WebSocket class
In this class, there is a collection to store all connections.
When a new connection comes in, in the onOpen() method, we will store the corresponding id for each connection as an identification. Assuming that the id has already been saved, the connection will be closed to prevent multiple logins with the same id.
When the connection is disconnected, move the connection out of the collection in the onClose() method.
When a new message is sent, it will traverse the connection, find the connection corresponding to the recipient's id, and then push the message to the recipient.

package org.antry.websocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @ClassName MyWebsocket
 * @Description TODO
 * @Autor TT
 * @Date 2020/10/10 11:02
 * @Version 1.0
 */
@ServerEndpoint(value = "/websocket/{user}")
@Component
public class MyWebsocket {
    
    
    // 通过类似GET请求方式传递参数的方法(服务端采用第二种方法"WebSocketHandler"实现)
//    websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");
    /**
     * 在线人数
     */
    public static AtomicInteger onlineNumber = new AtomicInteger(0);

    /**
     * 所有的对象,每次连接建立,都会将我们自己定义的MyWebsocket存放到List中,
     */
    public static List<MyWebsocket> webSockets = new CopyOnWriteArrayList<MyWebsocket>();

    /**
     * 会话,与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 每个会话的用户
     */
    private String user;
    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("user") String user) {
    
    
        System.err.println(user);
        if (user == null || "".equals(user)) {
    
    
            try {
    
    
                session.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
            return;
        }
        onlineNumber.incrementAndGet();
        for (MyWebsocket MyWebsocket : webSockets) {
    
    
            if (user.equals(MyWebsocket.user)) {
    
    
                try {
    
    
                    session.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

                return;
            }
        }
        this.session = session;
        this.user = user;
        webSockets.add(this);
    }
    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
    
    
        onlineNumber.decrementAndGet();
        webSockets.remove(this);
    }
    /**
     * 收到客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("user") String user) {
    
    
        System.err.println(message);
        String[] strArr = message.split("~");
        pushMessage(user,strArr[0],strArr[1]);
    }
    /**
     * 发送消息
     *
     * @param message 消息
     */
    public void sendMessage(String message) {
    
    
        try {
    
    
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 消息推送
     *
     * @param message
     * @param uuid    uuid为空则推送全部人员
     */
    public static void pushMessage(String user, String message, String uuid) {
    
    

        if (uuid == null || "".equals(uuid)) {
    
    
            for (MyWebsocket MyWebsocket : webSockets) {
    
    
                MyWebsocket.sendMessage(user + ":" + message);
            }
        } else {
    
    
            for (MyWebsocket MyWebsocket : webSockets) {
    
    
                if (uuid.equals(MyWebsocket.user)) {
    
    
                    MyWebsocket.sendMessage(message);
                }
            }
        }
    }
}

The difference between two simple front-end pages is that the receiving id and the sending id are different.
testOne.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天窗口1</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="public/dist/lib/uploader/zui.uploader.min.css" rel="stylesheet">

</head>
<body>
    <div>
        <div class="input-group">
            <input type="text" class="form-control" id="msg">
            <span class="input-group-btn">
        <button class="btn btn-default" type="button" onclick="send()">发送</button>
  </span>
        </div>
    </div>

<script src="public/dist/lib/jquery/jquery.js"></script>
<script src="public/dist/js/zui.min.js"></script>
<script src="public/layer/layer.js"></script>
<script src="public/js/function.js"></script>
<script src="public/js/testOne.js"></script>
</body>
</html>

testOne.js

var websocket = null;
var sendId = 0;
var receiveId = 1;
//---------------------------
/**
 * 初始化websocket
 * @param id
 */
doInit(sendId)
function doInit(sendId){
    
    
    if ('WebSocket' in window) {
    
    
        websocket = new WebSocket("ws:localhost:10086/websocket/" + sendId);
    } else {
    
    
        alert("浏览器不支持");
    }
    websocket.onopen = function () {
    
    
        addMessage("webscoket已经连接成功");
    };
    websocket.onclose = function () {
    
    
        addMessage("webscoket连接失败");
    };
    websocket.onmessage = function (event) {
    
    
        alert("收到消息:"+event.data);
    };
    websocket.onerror = function () {
    
    
        addMessage("webscoket连接失败");
    };
}
/**
 * 发送一条消息
 */
function send(){
    
    
    websocket.send(value('msg')+"~"+receiveId);
}
/**
 * 测试打印调试信息用
 * @param msg
 */
function  addMessage(msg) {
    
    
    console.log(msg)
}

testTwo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天窗口2</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="public/dist/lib/uploader/zui.uploader.min.css" rel="stylesheet">
</head>
<body>
<div>
    <div class="input-group">
        <input type="text" class="form-control" id="msg">
        <span class="input-group-btn">
        <button class="btn btn-default" type="button" onclick="send()">发送</button>
  </span>
    </div>
</div>

<script src="public/dist/lib/jquery/jquery.js"></script>
<script src="public/dist/js/zui.min.js"></script>
<script src="public/layer/layer.js"></script>
<script src="public/js/function.js"></script>
<script src="public/js/testTwo.js"></script>
</body>
</html>

testTwo.js

var websocket = null;
var sendId = 1;
var receiveId = 0;
//---------------------------
/**
 * 初始化websocket
 * @param id
 */
doInit(sendId)
function doInit(receiveId){
    
    
    if ('WebSocket' in window) {
    
    
        websocket = new WebSocket("ws:localhost:10086/websocket/" + sendId);
    } else {
    
    
        alert("浏览器不支持");
    }
    websocket.onopen = function () {
    
    
        addMessage("webscoket已经连接成功");
    };
    websocket.onclose = function () {
    
    
        addMessage("webscoket连接失败");
    };
    websocket.onmessage = function (event) {
    
    
        alert("收到消息:"+event.data);
    };
    websocket.onerror = function () {
    
    
        addMessage("webscoket连接失败");
    };
}
/**
 * 发送一条消息
 */
function send(){
    
    
    websocket.send(value('msg')+"~"+receiveId);
}
/**
 * 测试打印调试信息用
 * @param msg
 */
function  addMessage(msg) {
    
    
    console.log(msg)
}

Use two browsers to open these two pages to visit. The result is as follows
Insert picture description here

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_39150049/article/details/109306490