从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代

作者:常利兵日期:2026/4/21

从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代

引言:实时通信的需求与挑战

在当今数字化时代,互联网应用的实时交互需求日益增长。从在线聊天、股票行情实时更新,到多人协作办公、在线游戏等场景,实时通信已成为提升用户体验和业务效率的关键因素。传统的 HTTP 协议基于请求 - 响应模式,客户端发起请求,服务器被动响应,这种模式在实时通信场景中存在诸多局限性,如高延迟、高开销以及单向性(服务器无法主动推送数据,需客户端轮询) 。为了满足实时通信的需求,WebSocket 技术应运而生,它以独特的优势为现代互联网应用提供了高效、低延迟的实时通信解决方案,成为了众多开发者构建实时应用的首选技术之一。

WebSocket 技术基础

(一)什么是 WebSocket

WebSocket 是一种基于 TCP 的全双工通信协议,于 2011 年被 IETF 标准化为 RFC 6455 ,并由 W3C 制定了相应的 API 标准。与传统 HTTP 的单向请求 - 响应模式不同,WebSocket 允许客户端和服务器在建立一次连接后,就可以相互主动发送和接收数据,实现真正意义上的双向实时通信。举个例子,在在线聊天场景中,使用 HTTP 协议时,客户端需要不断发送请求获取新消息,而 WebSocket 则能让服务器在有新消息时直接推送给客户端,无需客户端频繁请求,大大提高了通信效率和实时性 。

(二)WebSocket 与 HTTP 的区别

  1. 通信方式:HTTP 是单向通信,客户端发起请求,服务器响应,服务器无法主动向客户端推送数据;WebSocket 是全双工通信,连接建立后,客户端和服务器可随时双向发送数据。
  2. 连接状态:HTTP 是无状态协议,每次请求都是独立的,服务器不保存客户端状态信息;WebSocket 是有状态协议,连接建立后会维持状态,服务器能识别客户端身份和状态。
  3. 连接建立:HTTP 基于请求 - 响应模式,一次请求 - 响应后连接通常关闭(除非使用 Keep - Alive);WebSocket 通过 HTTP 协议进行握手,握手成功后建立持久连接,后续基于此连接进行双向通信。
  4. 适用场景:HTTP 适用于获取静态资源、普通网页浏览等客户端主动获取数据的场景;WebSocket 适用于实时聊天、在线游戏、股票行情实时推送、实时监控等需要实时双向通信的场景。

(三)WebSocket 工作原理

  1. 握手阶段:客户端向服务器发送一个特殊的 HTTP 请求,请求头包含Upgrade: websocketConnection: Upgrade,表示希望将连接升级为 WebSocket 协议,同时还包含一个经过 Base64 编码的随机字符串Sec - WebSocket - Key用于安全验证。服务器接收到请求后,若支持 WebSocket 协议,会对Sec - WebSocket - Key进行处理(将其与固定字符串258EAFA5 - E914 - 47DA - 95CA - C5AB0DC85B11拼接,进行 SHA - 1 哈希计算,再进行 Base64 编码),生成Sec - WebSocket - Accept,并返回HTTP 101 Switching Protocols响应,包含Upgrade: websocketConnection: Upgrade以及Sec - WebSocket - Accept等头部信息,完成握手,连接升级为 WebSocket 连接。
  2. 数据传输阶段:握手成功后,客户端和服务器通过该 TCP 连接进行双向数据传输。WebSocket 的数据传输以帧为单位,数据帧包含FIN(表示是否是消息的最后一个片段)、RSV1RSV2RSV3(通常为 0 ,用于自定义扩展)、Opcode(表示数据类型,如文本、二进制、关闭连接、ping、pong 等)、Mask(客户端发送数据时必须设置为 1 ,用于掩码处理)、Payload length(数据负载长度)、Masking - key(掩码密钥,当Mask为 1 时存在)和Payload data(实际传输的数据)等字段。通过这种数据帧格式,双方可以高效地进行实时数据交互 。

Spring Boot 集成 WebSocket 实战

了解了 WebSocket 的基础知识后,接下来我们通过一个具体的 Spring Boot 项目实战,深入学习如何在 Spring Boot 中集成和使用 WebSocket ,实现一个简单的实时消息推送功能。

(一)创建 Spring Boot 项目

首先,我们使用 Spring Initializr 来快速创建一个 Spring Boot 项目。打开浏览器,访问https://start.spring.io/ ,在页面中进行如下配置:

  1. 项目基本信息:选择 Maven 项目,语言为 Java ,Spring Boot 版本根据需求选择(这里以最新稳定版本为例),填写 Group 和 Artifact 等基本信息。
  2. 依赖选择:在依赖搜索框中,依次添加 “Spring Web” 和 “Spring WebSocket” 依赖,这两个依赖分别用于支持 Web 开发和 WebSocket 功能。点击 “Generate” 按钮,下载生成的项目压缩包,解压后用 IDE(如 IntelliJ IDEA、Eclipse 等)打开。

(二)配置 WebSocket

在项目中创建一个配置类,用于启用 WebSocket 支持并注册 WebSocket 端点。在src/main/java/your - package - name下创建WebSocketConfig.java文件,代码如下:

1
2import org.springframework.context.annotation.Configuration;
3import org.springframework.web.socket.config.annotation.EnableWebSocket;
4import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
5import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
6
7@Configuration
8@EnableWebSocket
9public class WebSocketConfig implements WebSocketConfigurer {
10
11    @Override
12    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
13        // 注册一个WebSocket处理器,映射到/ws路径,允许所有来源的跨域请求
14        registry.addHandler(myWebSocketHandler(), "/ws").setAllowedOrigins("*"); 
15    }
16
17    // 定义一个WebSocket处理器的Bean
18    @Bean
19    public MyWebSocketHandler myWebSocketHandler() {
20        return new MyWebSocketHandler();
21    }
22}
23

在上述代码中,@Configuration注解表明这是一个配置类,@EnableWebSocket注解启用了 WebSocket 功能。registerWebSocketHandlers方法中,我们通过registry.addHandler注册了一个自定义的 WebSocket 处理器MyWebSocketHandler,并将其映射到/ws端点,同时通过setAllowedOrigins("*")允许所有来源的跨域请求(在生产环境中应根据实际情况限制跨域来源) 。

(三)编写 WebSocket 处理器

创建一个自定义的 WebSocket 处理器类,用于处理 WebSocket 连接的打开、消息接收、连接关闭等事件。在src/main/java/your - package - name下创建MyWebSocketHandler.java文件,代码如下:

1
2import org.springframework.web.socket.TextMessage;
3import org.springframework.web.socket.WebSocketSession;
4import org.springframework.web.socket.handler.TextWebSocketHandler;
5
6import java.util.concurrent.CopyOnWriteArraySet;
7
8public class MyWebSocketHandler extends TextWebSocketHandler {
9
10    // 使用线程安全的集合来存储WebSocket会话
11    private static final CopyOnWriteArraySet<WebSocketSession> sessions = new CopyOnWriteArraySet<>();
12
13    @Override
14    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
15        sessions.add(session);
16        System.out.println("新连接建立: " + session.getId());
17        session.sendMessage(new TextMessage("欢迎连接到WebSocket服务器,当前在线人数: " + sessions.size()));
18    }
19
20    @Override
21    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
22        String payload = message.getPayload();
23        System.out.println("收到消息: " + payload);
24        // 广播消息给所有连接的客户端
25        for (WebSocketSession webSocketSession : sessions) {
26            if (webSocketSession.isOpen()) {
27                webSocketSession.sendMessage(new TextMessage("来自 " + session.getId() + " 的消息: " + payload));
28            }
29        }
30    }
31
32    @Override
33    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
34        sessions.remove(session);
35        System.out.println("连接关闭: " + session.getId());
36        if (sessions.size() > 0) {
37            for (WebSocketSession webSocketSession : sessions) {
38                if (webSocketSession.isOpen()) {
39                    webSocketSession.sendMessage(new TextMessage("用户 " + session.getId() + " 已离开,当前在线人数: " + sessions.size()));
40                }
41            }
42        }
43    }
44}
45

在这个处理器类中:

  • afterConnectionEstablished方法在 WebSocket 连接建立后被调用,将新的会话添加到 sessions集合中,并向客户端发送欢迎消息,包含当前在线人数。
  • handleTextMessage方法在接收到客户端发送的文本消息时被调用,获取消息内容并广播给所有在线的客户端,消息中包含发送者的会话 ID 。
  • afterConnectionClosed方法在 WebSocket 连接关闭时被调用,将会话从 sessions集合中移除,并向其他在线客户端发送通知消息,告知有用户离开以及当前在线人数。

(四)前端页面示例

为了测试 WebSocket 功能,我们创建一个简单的 HTML 页面,通过 JavaScript 连接到后端的 WebSocket 服务。在src/main/resources/static目录下创建index.html文件,代码如下:

1
2<!DOCTYPE html>
3<html lang="zh-CN">
4<head>
5    <meta charset="UTF-8">
6    <title>WebSocket测试</title>
7</head>
8<body>
9    <h2>WebSocket实时通信测试</h2>
10    <input type="text" id="message" placeholder="输入消息">
11    <button onclick="sendMessage()">发送</button>
12    <div id="response"></div>
13
14    <script>
15        const socket = new WebSocket('ws://localhost:8080/ws');
16
17        socket.onopen = function () {
18            console.log('已连接到WebSocket服务器');
19        };
20
21        socket.onmessage = function (event) {
22            document.getElementById('response').innerHTML += event.data + '<br>';
23        };
24
25        socket.onclose = function () {
26            console.log('WebSocket连接已关闭');
27        };
28
29        function sendMessage() {
30            const message = document.getElementById('message').value;
31            socket.send(message);
32            document.getElementById('message').value = '';
33        }
34    </script>
35</body>
36</html>
37

在这个 HTML 页面中:

  • 通过new WebSocket('ws://localhost:8080/ws')创建一个 WebSocket 连接,指向后端的/ws端点。
  • onopen事件在连接建立时触发,在控制台打印连接成功信息。
  • onmessage事件在接收到服务器发送的消息时触发,将消息显示在页面的response区域。
  • onclose事件在连接关闭时触发,在控制台打印连接关闭信息。
  • sendMessage函数在用户点击 “发送” 按钮时被调用,获取输入框中的消息并通过 WebSocket 发送给服务器。

启动 Spring Boot 应用,打开浏览器访问http://localhost:8080/index.html,在页面中输入消息并点击发送,即可看到消息实时推送到页面,同时其他连接到该 WebSocket 服务的客户端也能收到广播消息,实现了简单的实时通信功能。

应用场景与案例分析

(一)常见应用场景

  1. 即时通讯:如微信、QQ 等社交软件的网页版或在线客服系统,WebSocket 能够实现消息的即时收发,提升沟通效率和用户体验,让用户感觉就像面对面交流一样,消息几乎无延迟 。
  2. 在线游戏:在多人在线游戏中,玩家的操作(如移动、攻击、释放技能等)需要实时同步给其他玩家。WebSocket 的低延迟和双向通信特性确保了游戏状态的及时更新,保证游戏的流畅性和公平性,避免因通信延迟导致的游戏体验不佳 。
  3. 实时监控:在工业监控、服务器状态监控、智能安防监控等场景中,WebSocket 可将监控数据实时推送给监控中心或管理人员。例如,工厂中的设备运行数据(温度、压力、转速等)可以通过 WebSocket 实时传输到监控大屏,一旦设备出现异常,能够立即发出警报,便于及时采取措施 。
  4. 股票行情与金融交易:股票交易平台、外汇交易平台等金融应用中,市场行情(股票价格、汇率等)瞬息万变。WebSocket 能实时推送最新的价格数据和交易信息,让投资者及时了解市场动态,做出准确的投资决策,抓住瞬息即逝的投资机会 。
  5. 协同办公:多人在线协作编辑文档、表格、思维导图等场景下,WebSocket 可以实时同步用户的操作,如文字输入、格式调整、图形绘制等,使团队成员能够实时看到彼此的修改,就像在同一时间、同一地点办公一样,提高协作效率 。

(二)案例展示

以某电商平台的实时订单提醒功能为例,该电商平台每天会产生大量订单,商家需要及时了解新订单的情况,以便快速处理订单,提高客户满意度。

  1. 需求分析:商家在登录电商平台的管理后台后,希望能够实时收到新订单的提醒消息,包括订单编号、下单时间、商品信息、客户信息等,无需手动刷新页面查询订单。
  2. 技术选型:后端采用 Spring Boot 作为开发框架,利用其强大的生态和便捷的开发特性;通信协议选择 WebSocket,以实现服务器主动向商家客户端推送订单消息。
  3. 实现方案
    • 后端实现:在 Spring Boot 项目中,按照前文所述的集成 WebSocket 的步骤,配置 WebSocket 并编写处理器。当有新订单生成时,订单服务会将订单信息发送给 WebSocket 处理器,处理器将订单消息广播给所有连接的商家客户端。例如,在订单创建的业务逻辑中添加如下代码:
1
2@Service
3public class OrderService {
4
5    @Autowired
6    private WebSocketServer webSocketServer;
7
8    public void createOrder(Order order) {
9        // 保存订单到数据库等操作
10        //...
11        // 发送订单提醒消息
12        webSocketServer.sendMessageToAll("新订单提醒:订单编号 " + order.getOrderId() + ",下单时间 " + order.getCreateTime() + ",商品:" + order.getProductList());
13    }
14}
15
  • 前端实现:商家管理后台的前端页面通过 JavaScript 创建 WebSocket 连接到后端的 WebSocket 服务。当接收到新订单提醒消息时,在页面上以弹窗或消息列表的形式展示订单信息。例如:
1
2<!DOCTYPE html>
3<html lang="zh-CN">
4<head>
5    <meta charset="UTF-8">
6    <title>电商平台商家后台</title>
7</head>
8<body>
9    <h2>电商平台商家后台</h2>
10    <div id="order-notification"></div>
11
12    <script>
13        const socket = new WebSocket('ws://localhost:8080/ws/order');
14
15        socket.onopen = function () {
16            console.log('已连接到订单提醒服务');
17        };
18
19        socket.onmessage = function (event) {
20            const notificationDiv = document.getElementById('order - notification');
21            const message = document.createElement('div');
22            message.textContent = event.data;
23            notificationDiv.appendChild(message);
24        };
25
26        socket.onclose = function () {
27            console.log('订单提醒服务连接已关闭');
28        };
29    </script>
30</body>
31</html>
32

通过这样的实现,商家在使用电商平台管理后台时,能够实时收到新订单提醒,大大提高了订单处理效率,减少了订单处理的延迟,提升了客户满意度和商家运营效率 。

优化与扩展

(一)性能优化

  1. 设置合理的连接超时时间:在 WebSocket 配置中,设置合适的连接超时时间可以避免无效连接占用资源。例如,在 Spring Boot 中,可以通过application.properties文件配置:spring.websocket.timeout=60000(单位为毫秒,这里设置为 60 秒) ,表示如果 60 秒内没有数据传输,连接将被关闭。也可以在 WebSocket 配置类中通过setHandshakeTimeout方法进行设置,如:
1
2@Configuration
3@EnableWebSocket
4public class WebSocketConfig implements WebSocketConfigurer {
5    @Override
6    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
7        registry.addHandler(myWebSocketHandler(), "/ws")
8              .setAllowedOrigins("*")
9              .setHandshakeTimeout(60000); 
10    }
11    //...
12}
13
  1. 优化消息处理逻辑:避免在消息处理方法中执行耗时操作,如复杂的数据库查询、大规模数据计算等。如果有耗时任务,可将其放入异步线程池中执行,避免阻塞 WebSocket 连接线程。例如,使用 Spring 的@Async注解将消息处理方法标记为异步方法:
1
2import org.springframework.scheduling.annotation.Async;
3import org.springframework.stereotype.Component;
4
5@Component
6public class MyWebSocketHandler extends TextWebSocketHandler {
7
8    @Async
9    @Override
10    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
11        // 处理消息逻辑,这里的耗时操作将在异步线程中执行
12        String payload = message.getPayload();
13        //...
14    }
15}
16
  1. 使用心跳机制:WebSocket 协议本身没有内置的保持活动功能,可通过发送定期的心跳消息来防止连接被关闭。在 Spring 中,可以扩展TextWebSocketHandler并覆盖其方法以定义自定义心跳逻辑。例如,每隔一段时间(如 30 秒)向客户端发送一个 ping 消息,客户端接收到 ping 消息后返回 pong 消息,服务器通过判断是否收到 pong 消息来确定连接是否正常:
1
2import org.springframework.web.socket.CloseStatus;
3import org.springframework.web.socket.TextMessage;
4import org.springframework.web.socket.WebSocketSession;
5import org.springframework.web.socket.handler.TextWebSocketHandler;
6
7import java.util.concurrent.Executors;
8import java.util.concurrent.ScheduledExecutorService;
9import java.util.concurrent.TimeUnit;
10
11public class HeartbeatWebSocketHandler extends TextWebSocketHandler {
12
13    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
14    private static final long HEARTBEAT_INTERVAL = 30; 
15
16    @Override
17    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
18        super.afterConnectionEstablished(session);
19        startHeartbeat(session);
20    }
21
22    @Override
23    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
24        super.afterConnectionClosed(session, status);
25        stopHeartbeat(session);
26    }
27
28    private void startHeartbeat(WebSocketSession session) {
29        scheduler.scheduleAtFixedRate(() -> {
30            if (session.isOpen()) {
31                try {
32                    session.sendMessage(new TextMessage("ping")); 
33                } catch (Exception e) {
34                    e.printStackTrace();
35                    try {
36                        session.close();
37                    } catch (Exception ex) {
38                        ex.printStackTrace();
39                    }
40                }
41            }
42        }, 0, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
43    }
44
45    private void stopHeartbeat(WebSocketSession session) {
46        scheduler.shutdownNow();
47    }
48
49    @Override
50    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
51        String payload = message.getPayload();
52        if ("pong".equals(payload)) { 
53            // 接收到pong消息,说明连接正常
54        } else {
55            super.handleTextMessage(session, message);
56        }
57    }
58}
59

(二)功能扩展

  1. 使用 STOMP 协议增强消息能力:STOMP(Simple Text Oriented Messaging Protocol)是一种简单的面向文本的消息协议,可在 WebSocket 上使用,为 WebSocket 通信提供了更丰富的消息处理能力,如消息的订阅与发布、消息的广播、点对点消息发送等。在 Spring Boot 中,通过@EnableWebSocketMessageBroker注解启用消息代理,并配置相关的消息前缀等。例如:
1
2import org.springframework.context.annotation.Configuration;
3import org.springframework.messaging.simp.config.MessageBrokerRegistry;
4import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
5import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
6import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
7
8@Configuration
9@EnableWebSocketMessageBroker
10public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {
11
12    @Override
13    public void configureMessageBroker(MessageBrokerRegistry config) {
14        config.enableSimpleBroker("/topic", "/queue"); 
15        config.setApplicationDestinationPrefixes("/app"); 
16    }
17
18    @Override
19    public void registerStompEndpoints(StompEndpointRegistry registry) {
20        registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS(); 
21    }
22}
23

在上述配置中,/topic用于广播消息(所有订阅该主题的客户端都能收到消息),/queue用于点对点消息(只有特定的接收者能收到消息),/app是客户端发送消息的前缀 。客户端可以通过/app前缀发送消息到服务器,服务器处理后通过/topic/queue前缀将消息发送给相应的客户端。
2. 实现消息格式结构化:为了更好地处理不同类型的消息,提高消息的可读性和可维护性,可以将消息格式结构化,例如使用 JSON 格式。在消息发送端,将消息对象转换为 JSON 字符串发送,在接收端,将接收到的 JSON 字符串解析为消息对象。例如,定义一个消息类:

1
2import com.fasterxml.jackson.annotation.JsonProperty;
3
4public class Message {
5    @JsonProperty("type")
6    private String type; 
7    @JsonProperty("content")
8    private String content; 
9
10    public Message(String type, String content) {
11        this.type = type;
12        this.content = content;
13    }
14
15    // Getter  Setter 方法
16    public String getType() {
17        return type;
18    }
19
20    public void setType(String type) {
21        this.type = type;
22    }
23
24    public String getContent() {
25        return content;
26    }
27
28    public void setContent(String content) {
29        this.content = content;
30    }
31}
32

在发送消息时:

1
2import com.fasterxml.jackson.databind.ObjectMapper;
3import org.springframework.web.socket.TextMessage;
4import org.springframework.web.socket.WebSocketSession;
5
6public class MyWebSocketHandler extends TextWebSocketHandler {
7
8    @Override
9    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
10        String payload = message.getPayload();
11        ObjectMapper objectMapper = new ObjectMapper();
12        Message msg = objectMapper.readValue(payload, Message.class);
13        // 根据消息类型进行不同的处理
14        if ("chat".equals(msg.getType())) {
15            // 处理聊天消息
16        } else if ("system".equals(msg.getType())) {
17            // 处理系统消息
18        }
19    }
20}
21
  1. 持久化聊天记录:如果应用需要保存聊天记录,以便用户后续查看历史聊天内容,可以将聊天消息存储到数据库中。使用 Spring Data JPA 等 ORM 框架,定义消息实体类和对应的 Repository 接口,在消息处理方法中,将消息保存到数据库。例如,定义消息实体类:
1
2import javax.persistence.Entity;
3import javax.persistence.GeneratedValue;
4import javax.persistence.GenerationType;
5import javax.persistence.Id;
6import java.time.LocalDateTime;
7
8@Entity
9public class ChatMessage {
10    @Id
11    @GeneratedValue(strategy = GenerationType.IDENTITY)
12    private Long id;
13    private String sender;
14    private String receiver;
15    private String content;
16    private LocalDateTime timestamp;
17
18    // 构造方法、Getter  Setter 方法
19    public ChatMessage() {
20    }
21
22    public ChatMessage(String sender, String receiver, String content, LocalDateTime timestamp) {
23        this.sender = sender;
24        this.receiver = receiver;
25        this.content = content;
26        this.timestamp = timestamp;
27    }
28
29    public Long getId() {
30        return id;
31    }
32
33    public void setId(Long id) {
34        this.id = id;
35    }
36
37    public String getSender() {
38        return sender;
39    }
40
41    public void setSender(String sender) {
42        this.sender = sender;
43    }
44
45    public String getReceiver() {
46        return receiver;
47    }
48
49    public void setReceiver(String receiver) {
50        this.receiver = receiver;
51    }
52
53    public String getContent() {
54        return content;
55    }
56
57    public void setContent(String content) {
58        this.content = content;
59    }
60
61    public LocalDateTime getTimestamp() {
62        return timestamp;
63    }
64
65    public void setTimestamp(LocalDateTime timestamp) {
66        this.timestamp = timestamp;
67    }
68}
69

定义 Repository 接口:

1
2import org.springframework.data.jpa.repository.JpaRepository;
3
4public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {
5}
6

在消息处理方法中保存消息:

1
2import org.springframework.beans.factory.annotation.Autowired;
3import org.springframework.web.socket.TextMessage;
4import org.springframework.web.socket.WebSocketSession;
5import org.springframework.web.socket.handler.TextWebSocketHandler;
6
7import java.time.LocalDateTime;
8
9public class MyWebSocketHandler extends TextWebSocketHandler {
10
11    @Autowired
12    private ChatMessageRepository chatMessageRepository;
13
14    @Override
15    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
16        String payload = message.getPayload();
17        // 假设消息格式为 "sender:receiver:content"
18        String[] parts = payload.split(":", 3);
19        String sender = parts[0];
20        String receiver = parts[1];
21        String content = parts[2];
22
23        ChatMessage chatMessage = new ChatMessage(sender, receiver, content, LocalDateTime.now());
24        chatMessageRepository.save(chatMessage); 
25    }
26}
27

从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代》 是转载文章,点击查看原文


相关推荐


公网 IP、私网 IP、路由表、转发表与 MAC 地址的关系
小红的布丁2026/4/12

引言 学习网络时,最容易混淆的不是协议流程,而是几个看起来相近、其实不在一个层面的概念,比如: 私网 IP 和公网 IP路由表和转发表“在链路上”到底是什么意思MAC 地址和 IP 地址分别属于哪一层 这篇文章把这些概念放到同一条线上梳理清楚,尽量用能直接形成画面的方式去理解。 私网 IP、公网 IP 和 NAT 到底是什么关系 很多人第一次接触家庭网络时,容易把 192.168.x.x 这类地址叫成“虚拟 IP”。这个说法不够准确,更准确的叫法应该是: 公网 IP私网 IPNAT 什么是公网


大模型推理凭什么这么贵?从GRPO到BCR,推理效率之战全解析
陆业聪2026/4/4

一个反常识的观察:推理越强,账单越贵 DeepSeek-R1 横空出世之后,"o1-style 推理"几乎成了大模型进化的标配动作。CoT、长思考链、自我反思……这些机制确实让模型在数学、代码、逻辑推理上表现亮眼。但随之而来的问题是——每一次"深度思考",都是在烧钱。 最极端的案例:让 o1 解一道简单的小学数学题,它会生成好几百个 token 的"内心独白",然后给出一个一眼就能看出来的答案。这就像雇了个博士级顾问,让他帮你决定午饭吃什么——能力没问题,但成本不对等。 这个问题的本质不是"推理


SwiftUI 如何实现 Infinite Scroll?
RickeyBoy2026/3/27

欢迎点个 star:github.com/RickeyBoy/R… 面试题:用 SwiftUI 实现一个无限滚动列表,支持分页加载。 这道题我在面试中遇到过好几次,说实话第一次答的时候以为随便写个 LazyVStack + onAppear 就完事了。后来才发现,面试官真正想考的不是你会不会用 API,而是你对状态管理、性能优化、Task 生命周期这些东西到底理解多深。 我的思路是从最简方案出发,一步步暴露问题、一步步优化。在开始写代码之前,先聊一下架构选型。 为什么选 MVVM? 先说一下


基于 Cloudflare 生态的 AI Agent 实现
Surmon2026/3/19

2026 新年的一个夜晚,窗外炮竹烟花争相闪耀,脑海里灵光一闪:我这快十年的老博客能不能也赶一波时髦,实现一个真正「有用」的智能助手? 有用 的意思是,它不能是一个只会随便聊天的机器人,而是一个真正了解我(博主)、了解博客内容的 AI 分身。它最好能事无巨细地知道我写过哪些文章,了解我的观点、立场和经历,能根据访客的问题去知识库里精准地找到最相关的内容,再结合上下文给出自然又富有意义的回答。 它应该是一张鲜活、灵动的个人名片。 这并不是一个多么复杂的需求,开源工具和商业基建也已经很成熟了,但真正


从零开发一个掘金自动发布 Skill,并上架 Clawhub
小巫debug日记2026/3/10

从零开发一个掘金自动发布 Skill,并上架 Clawhub 本文记录了一次完整的 Skill 开发旅程:从一句「帮我创建一个可以自动发布文章到掘金的 skill」开始,到最终成功上架 Clawhub,全程真实还原每一个关键决策和踩坑过程。 背景:为什么要做这个 Skill? 我日常运营一个 AI 资讯账号,每天需要将 Markdown 格式的文章发布到多个平台,包括微信公众号、小红书、掘金等。其中微信公众号和小红书已经有现成的 Skill 可以用,但掘金没有。 每次发布掘金都要: 打开


Word 中 MathType 启动慢、卡顿、卡死 | 由于某种原因,PowerPoint 无法加载MathType……
斐夷所非2026/3/2

注:本文为 “office 中 MathType 启动、加载异常” 相关合辑。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 Word 2013 中 MathType 窗口启动延迟问题分析与解决方案 香蕉君达 发布于 2026-02-19 12:12 1 现象描述 通过快捷键或功能区按钮在 Word 2013 中插入公式时,编辑窗口启动延迟时长约为 3~4 秒,对文档编辑流程造成干扰。 测试表明,若系统中已存在至少一个处于打开状态的 MathType 窗口,后续公式


SpringBoot多环境配置实战指南
北极的代码2026/2/22

前言:在之前的开发环境中要跟改配置,测试环境也要改,每次切换环境都要手动修改配置文件 常常发生"我们在本地能运行,怎么部署到服务器就报错"的情况,一不小心就把测试环境的配置提交到代码库。因此我们提出了多环境开发配置。 多环境开发配置: 在SpringBoot中,多环境配置的管理核心是利用Profile机制,它允许我们为不同的运行环境(开发,测试,生产)定义独立的配置,并在应用启动时动态的激活,从而实现配置等隔离与灵活切换。 核心实现方式:Profile 特定配置文件 总之就


聊一聊 CLI:为什么真正的工程能力,都藏在命令行里?
G探险者2026/2/14

大家好,我是G探险者! 今天我们来聊一聊CLI。 在很多人眼里,命令行(CLI,Command Line Interface)是“黑框 + 英文命令”的代名词。 对普通用户来说,它晦涩、难记、不友好。 但对工程师来说—— CLI 是系统可编排能力的起点,是自动化的基础设施,是 DevOps 的地基。 今天我们不从“怎么用命令”讲起,而是聊一聊: CLI 是怎么诞生的? 为什么它没有被 GUI 取代? 为什么所有现代基础设施几乎都优先设计 CLI? 为什么 CLI 是工程能力的分水岭?


你这一生到底该如何赚钱?
袁庭新2026/2/5

大家好,我是袁庭新。 赚钱是每个成年人每天的头等大事,那你有没有认真思考过:你这一辈子到底应该如何赚钱?根据这几年的总结,我认为赚钱的方式无非以下三种: 用时间赚钱 用金钱赚钱 用金钱和时间一起赚钱 这三种赚钱方式的回报是不一样的,它们依次越来越大,最牛的就是用“时间+金钱”赚钱。 我们绝大多数人一生摆脱不了“用时间赚钱”这种模式,想要获得更多回报就低拼命上班加班。但,用时间赚钱的方式是可以改良的,最核心的策略就是“想尽一切办法把自己的同一份时间出售很多次”,举几个例子吧,比如:讲课、写书


爷爷你关注的前端博主复活了!! 他学python去了??
jinzunqinjiu2026/1/27

如何配置python环境。 hello,兄弟们马上过年了,想死你们了。转眼间就已经毕业半年。也工作了快一年了。从实习生一路跌跌撞撞,从刚开始连react的状态依赖都老是写死循环到现在已经经历过很多项目了。说来这一年也有很多成长,参与了公司很多的项目,看过各种代码。最终在ai的加持下已经能够独挡一面。但是最近公司开始掀起了一股ai风,以及网上ai全栈的兴起,我想我是坐不住了。深耕前端 or 技术转型。 小孩子才做选择,前端为主ai为辅,所以我要开始学习python逐渐开始学习ai应用了。正好我也没

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客