SpringBoot整合Websocket实现聊天室

1.添加依赖:在pom.xml文件中添加WebSocket相关依赖。

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

2.创建WebSocket配置类:创建一个WebSocket配置类,用于配置WebSocket端点和处理器。

@Configuration
@EnableWebSocket
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpoint(){
        return new ServerEndpointExporter();
    }
}

3.创建WebSocket处理器:创建一个WebSocket处理器,用于处理WebSocket连接和消息。

@Component
@ServerEndpoint("/chat")
public class ChatService {
    /**
     * 连接会话池
     */
    private static ConcurrentHashMap<String, Session> SESSION_POOL = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session) throws IOException {
        // 判断客户端对象是否存在
        if (SESSION_POOL.containsKey(session.getQueryString())) {
            CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "ID冲突,连接拒绝");
            session.getUserProperties().put("reason", closeReason);
            session.close();
            return;
        }
        // 将客户端对象存储到会话池
        SESSION_POOL.put(session.getQueryString(), session);
        System.out.println("客户端(" + session.getQueryString() + "):开启了连接");
    }

    @OnMessage
    public String onMessage(String msg, Session session) throws IOException {
        // 解析消息 ==> ID::消息内容
        String[] msgArr = msg.split("::", 2);
        // 处理群发消息,ID==all表示群发
        if ("all".equalsIgnoreCase(msgArr[0])) {
            for (Session one : SESSION_POOL.values()) {
                // 排除自己
                if (one == session) {
                    continue;
                }
                // 发送消息
                one.getBasicRemote().sendText(msgArr[1]);
            }
        }
        // 指定发送
        else {
            // 获取接收方
            Session target = SESSION_POOL.get(msgArr[0]);
            if (target != null) {
                target.getBasicRemote().sendText(msgArr[1]);
            }
        }
        return session.getQueryString() + ":消息发送成功";
    }

    @OnClose
    public void onClose(Session session) {
        // 连接拒绝关闭会话
        Object reason = session.getUserProperties().get("reason");
        if (reason instanceof CloseReason) {
            CloseReason creason = (CloseReason) reason;
            if (creason.getCloseCode() == CloseReason.CloseCodes.CANNOT_ACCEPT) {
                System.out.println("拒绝客户(" + session.getQueryString() + "):关闭连接");
                return;
            }
        }
        // 从会话池中移除会话
        SESSION_POOL.remove(session.getQueryString());
        System.out.println("客户端(" + session.getQueryString() + "):关闭连接");
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("客户端(" + session.getQueryString() + ")错误信息:" + throwable.getMessage());
    }

    @SneakyThrows
    public void sendMessage(String id, Object message) {
        // 群发
        if ("all".equalsIgnoreCase(id)) {
            for (Session one : SESSION_POOL.values()) {
                // 发送消息
                one.getBasicRemote().sendText(JSONUtil.toJsonStr(message));
            }
        }
        // 指定发送
        else {
            // 获取接收方
            Session target = SESSION_POOL.get(id);
            if (target != null) {
                target.getBasicRemote().sendText(JSONUtil.toJsonStr(message));
            }
        }
    }
}

4.创建HTML页面:创建一个HTML页面,用于在浏览器中连接WebSocket并进行聊天。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室客户端</title>
</head>
<body>
<h1>Chat Client</h1>
<div>
    <input id="clientId" placeholder="输入ID" value="1">
    <input onclick="init()" value="连接服务器" type="button"><br><br>
    <input id="receiverId" placeholder="输入接收人ID" value="all"><br><br>
    <textarea id="message" style="margin: 0; height: 197px; width: 362px;"
              placeholder="发送消息内容"></textarea><br>
    <input onclick="send()" value="发送消息" type="button">
    <input onclick="closeConnect()" value="关闭连接" type="button">
</div>
<div id="output"></div>
<script type="text/javascript" language="JavaScript">
    //屏幕回显输出方法
    function writeToScreen(message) {
        let pre = document.createElement("p");
        pre.style.wordWrap = "break-word";
        pre.innerHTML = message;
        document.getElementById("output").appendChild(pre);
    }

    //初始化websocket
    let echo_websocket;
    function init() {
        let clientId = document.getElementById("clientId").value;
        let wsUri = "ws://localhost:10500/chat?" + clientId;
        writeToScreen("连接到" + wsUri);
        //1.创建WebSocket客户端对象
        echo_websocket = new WebSocket(wsUri);
        //2.开门握手完成回调
        echo_websocket.onopen = function (evt) {
            console.log(evt);
            writeToScreen("连接打开成功 !");
        };
        //3.监听服务端的消息
        echo_websocket.onmessage = function (evt) {
            writeToScreen("接收服务端消息:<br> " + evt.data);
        };
        //4.如果连接中断
        echo_websocket.onerror = function (evt) {
            writeToScreen('<span style="color: red;">ERROR:'+evt.data+'</span>');
            //关闭连接
            closeConnect();
        };
        //5.注册close事件
        echo_websocket.onclose = function(evt){
            writeToScreen('<span style="color: green;">INFO:关闭连接</span> ');
            if(evt.reason){
                writeToScreen
                (`<span style="color: red;">错误信息:${evt.reason}</span> `);
            }
        }
    }

    //6.向服务发送消息
    function send() {
        let message = document.getElementById("message").value;
        let receiver = document.getElementById("receiverId").value;
        echo_websocket.send(receiver + "::" + message);
        writeToScreen("发送消息: " + message);
    }

    //7.如果不需要通讯,那么关闭连接
    function closeConnect() {
        echo_websocket.close();
    }
</script>
</body>
</html>