WebSocket(一)Spring框架WebSocket

252 阅读5分钟

Spring框架WebSocket

在一个项目中若要实现实时通信,那么websocket必然是首选,如果还使用传统的http请求,无效请求多,响应时间久,服务器压力大,很难适应现实开发需求,而WebSocket则可以完美解决这些问题。

WebSocket的优点

  1. 实现双向通信: WebSocket支持服务器与客户端之间的全双向通信,双方可以随时主动发送消息,实时接收。
  2. 低延迟: 连接建立后,WebSocket保持长连接,消息传输无需重复建立HTTP连接,减少了开销。
  3. 传输效率高: WebSocket协议的头部信息较小,相比于HTTP请求的复杂头部,数据传输效率更高。
  4. 状态保持: WebSocket连接是持久的,服务器可以维护客户端状态(如会话、用户信息),无需每次请求都携带信息。
  5. 服务器推送: 服务器主动向客户端推送消息,无需客户端轮询。

常见的WebSocket分为两种使用方法

  1. Spring WebSocket,基于Spring框架提供的原生WebSocket支持,适合SpringBoot项目。
  2. JSR-356,基于Java EE的WebSocket标准API,更适合非Spring或混合技术栈项目。

这篇文章要讲的便是第一种WebSocket的基本的使用方式:Spring WebSocket。

Spring WebSocket

一、引入依赖

  1. 首先创建一个Maven,SpringBoot项目。
  2. 打开pom文件,添加如下Spring Web和Spring WebSocket依赖。
  3. 刷新maven项目,确保依赖引入成功。
<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>

二、配置WebSocket

我们需要配置SpringBoot的WebSocket支持,定义WebSocket的端点(endpoint)。 定义WebSocket端点指的是创建一个特定URL,客户端可以通过该URL与服务器建立通信。

  1. 创建一个SpringBoot配置类,我这里取名叫WebSocketConfig,需要实现WebSocketConfigurer接口。
  2. 在类上加EnableWebSocket注解,启用Spring的WebSocket支持。
  3. 重写接口的registerWebSocketHandlers方法,往registry中注册一个WebSocket处理类,添加端点,这里的"chat"就是定义的端点。
  4. 上述注册的WebSocket处理类目前还未定义,示例代码中即为ChatWebSocketHandler类,后面会创建该类。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        regisry.addHandler(new ChatWebSocketHandler(), "/chat")
               .setAllowedOrigins("*"); //运行跨域访问,前后端分离时使用
    }
}

三、创建WebSocket处理类

  1. 创建处理类,我这里叫ChatWebSocketHandler,这个类可以根据实际需求来定义。
  • 如果只需要文本数据的传输那么继承TextWebSocketHandler。
  • 如果需要传输图片、文件、音视频等二进制数据则继承BinaryWebSocketHandler。
  • 如果需要同时处理文本和二进制消息则继承AbstractWebSocketHandler,更加灵活。

此处我选择继承AbstractWebSocketHandler,因为最灵活,代码量和前两个相比也几乎没有增加。

  1. 定义一个hash用于存储所有活跃的客户端会话。
public class ChatWebSocketHandler extends AbstractWebSocketHandler {
    private final Set<WebSocketSession> sessions = new HashSet<>();
}
  1. 自定义广播给所有在线客户端的broadcastMessage方法,后续重写的方法中会调用
private void broadcastMessage(String message) throws IOException {
    TextMessage textMessage = new TextMessage(message);
    for (WebSocketSession s : sessions) {
        if (s.isOpen()) {
            s.sendMessage(textMessage);
        }
    }
}
  1. 重写afterConnectionEstablished方法,客户端连接时触发。
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    sessions.add(session);
    session.sendMessage(new TextMessage("欢迎来聊天!"));
    broadcastMessage("一位新用户加入聊天!");
}
  1. 重写handleTextMessage方法,处理某个客户端发给服务器的消息,广播给相应的在线客户端。
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    String payload = message.getPayload();
    broadcastMessage("用户:" + payload);
}
  1. 重写handleBinaryMessage,此处不做演示。
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
    //不做演示,需要自定义
}
  1. 重写afterConnectionClosed方法,当客户端退出连接时触发。
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    sessions.remove(session);
    broadcastMessage("系统:有一位用户退出聊天!");
}

四、前端编写

  1. 创建一个简单的html页面,让用户可以连接WebSocket、发送消息并显示聊天内容。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Simple Chat</title>
    <style>
        #chat-box {
            width: 300px;
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: scroll;
            margin-bottom: 10px;
            padding: 10px;
        }
        #message-input {
            width: 250px;
        }
    </style>
</head>
<body>
    <h2>Simple Chat</h2>
    <div id="chat-box"></div>
    <input type="text" id="message-input" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>

    <script>
        const ws = new WebSocket("ws://localhost:8080/chat");

        ws.onopen = function() {
            appendMessage("Connected to chat server!");
        };

        ws.onmessage = function(event) {
            appendMessage(event.data);
        };

        ws.onclose = function() {
            appendMessage("Disconnected from chat server!");
        };

        function sendMessage() {
            const input = document.getElementById("message-input");
            const message = input.value;
            if (message) {
                ws.send(message);
                input.value = "";
            }
        }

        function appendMessage(message) {
            const chatBox = document.getElementById("chat-box");
            const p = document.createElement("p");
            p.textContent = message;
            chatBox.appendChild(p);
            chatBox.scrollTop = chatBox.scrollHeight; // 自动滚动到底部
        }
    </script>
</body>
</html>
  • HTML:一个聊天框(chat-box)、输入框(message-input)、按钮
  • JavaScript:
    • 创建一个WebSocket,根据之前后端定义的端点进行连接
    • ws.onopen: 连接成功时逻辑
    • ws.onmessage: 服务器往客户端发送消息时的逻辑(服务器->客户端)
    • ws.onclose: 连接关闭时逻辑
    • sendMessage: 发送输入框中的消息(客户端->服务器)
    • appendMessage: 将消息追加到聊天框中展示

五、运行测试

  1. 启动SpringBoot项目
  2. 根据配置的端口,如果是8080,访问 http://localhost:8080 ,进入聊天界面。
  3. 打开多个浏览器(或者隐身窗口),访问 http://localhost:8080
  4. 在一个窗口发送消息,查看其他窗口是否实时接收到消息。
  5. 关闭窗口应该收到其他用户离开的通知。

扩展

以上教程比较基础,实际开发更加复杂,比如当前项目没有用户信息区分,没有时间区分,如果需要扩展,需要自定义Message类,包含用户及消息相关信息,然后序列化对象后通过TextMessage类发送消息到前端,在进行数据渲染。

  • 用户名: 消息上展示用户名
  • 私聊: 通过WebSocket消息携带目标用户ID,实现一对一聊天
  • 消息持久化: 将消息保存到数据库(如MySQL或MongoDB)