控制器示例
Controller public class WebSocketController { // 注入消息模板 Autowired private SimpMessagingTemplate messagingTemplate; /** * 处理客户端发送的消息 * 目的地/app/chat */ MessageMapping(/chat) SendTo(/topic/messages) public ChatMessage handleMessage(ChatMessage message) { message.setTimestamp(new Date()); System.out.println(收到消息: message.getContent()); return message; } /** * 发送广播消息 */ GetMapping(/broadcast) public void broadcast(String content) { ChatMessage message new ChatMessage(); message.setContent(content); message.setSender(系统); message.setTimestamp(new Date()); // 发送到 /topic/messages messagingTemplate.convertAndSend(/topic/messages, message); } /** * 发送点对点消息 */ GetMapping(/sendToUser) public void sendToUser(String userId, String content) { ChatMessage message new ChatMessage(); message.setContent(content); message.setSender(管理员); message.setTimestamp(new Date()); // 发送给指定用户/user/{userId}/queue/messages messagingTemplate.convertAndSendToUser( userId, /queue/messages, message ); } } // 消息实体类 Data AllArgsConstructor NoArgsConstructor public class ChatMessage { private String sender; private String content; private Date timestamp; }连接拦截器Component public class WebSocketInterceptor extends ChannelInterceptorAdapter { Override public Message? preSend(Message? message, MessageChannel channel) { StompHeaderAccessor accessor MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { // 连接建立时处理 String token accessor.getFirstNativeHeader(token); // 验证 token... System.out.println(用户连接: accessor.getSessionId()); } else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) { // 连接断开时处理 System.out.println(用户断开: accessor.getSessionId()); } return message; } }原生 Java WebSocketJSR 356注解方式ServerEndpoint(/chat/{userId}) Component public class ChatEndpoint { // 存储所有连接 private static final MapString, Session sessions new ConcurrentHashMap(); // 存储用户ID和session的映射 private static final MapString, String userSessionMap new ConcurrentHashMap(); /** * 连接建立时调用 */ OnOpen public void onOpen(Session session, PathParam(userId) String userId) { System.out.println(连接建立: session.getId() , 用户: userId); // 保存连接 sessions.put(session.getId(), session); userSessionMap.put(userId, session.getId()); // 通知其他用户有新用户上线 broadcast(系统, 用户 userId 上线了); } /** * 收到消息时调用 */ OnMessage public void onMessage(String message, Session session, PathParam(userId) String userId) { System.out.println(收到消息: message from: userId); try { // 解析消息 JSONObject json new JSONObject(message); String content json.getString(content); String toUserId json.optString(to, null); if (toUserId ! null) { // 私聊消息 sendToUser(userId, toUserId, content); } else { // 群发消息 broadcast(userId, content); } } catch (Exception e) { e.printStackTrace(); } } /** * 连接关闭时调用 */ OnClose public void onClose(Session session, PathParam(userId) String userId) { System.out.println(连接关闭: session.getId()); // 移除连接 sessions.remove(session.getId()); userSessionMap.remove(userId); // 通知其他用户 broadcast(系统, 用户 userId 下线了); } /** * 发生错误时调用 */ OnError public void onError(Session session, Throwable error) { System.out.println(连接错误: session.getId()); error.printStackTrace(); } /** * 广播消息给所有用户 */ private void broadcast(String sender, String content) { JSONObject message new JSONObject(); message.put(sender, sender); message.put(content, content); message.put(timestamp, System.currentTimeMillis()); message.put(type, broadcast); // 发送给所有连接的客户端 for (Session session : sessions.values()) { if (session.isOpen()) { try { session.getAsyncRemote().sendText(message.toString()); } catch (Exception e) { e.printStackTrace(); } } } } /** * 发送私聊消息 */ private void sendToUser(String fromUserId, String toUserId, String content) { String toSessionId userSessionMap.get(toUserId); if (toSessionId ! null) { Session toSession sessions.get(toSessionId); if (toSession ! null toSession.isOpen()) { try { JSONObject message new JSONObject(); message.put(sender, fromUserId); message.put(content, content); message.put(timestamp, System.currentTimeMillis()); message.put(type, private); toSession.getAsyncRemote().sendText(message.toString()); } catch (Exception e) { e.printStackTrace(); } } } } }编程方式继承 Endpoint 类ServerEndpoint(/game) public class GameEndpoint extends Endpoint { private static final SetSession sessions Collections.synchronizedSet(new HashSet()); Override public void onOpen(Session session, EndpointConfig config) { System.out.println(新连接: session.getId()); sessions.add(session); // 添加消息处理器 session.addMessageHandler(new MessageHandler.WholeString() { Override public void onMessage(String message) { System.out.println(收到: message); // 处理游戏逻辑 handleGameMessage(session, message); } }); // 发送欢迎消息 try { JSONObject welcome new JSONObject(); welcome.put(type, welcome); welcome.put(message, 欢迎加入游戏!); welcome.put(sessionId, session.getId()); session.getBasicRemote().sendText(welcome.toString()); } catch (IOException e) { e.printStackTrace(); } } Override public void onClose(Session session, CloseReason closeReason) { System.out.println(连接关闭: session.getId()); sessions.remove(session); // 通知其他玩家 broadcastPlayerLeft(session.getId()); } Override public void onError(Session session, Throwable thr) { System.err.println(连接错误: session.getId()); thr.printStackTrace(); } private void handleGameMessage(Session session, String message) { try { JSONObject json new JSONObject(message); String type json.getString(type); switch (type) { case move: // 处理移动 handlePlayerMove(session, json); break; case chat: // 处理聊天 handleChatMessage(session, json); break; default: System.out.println(未知消息类型: type); } } catch (Exception e) { e.printStackTrace(); } } private void handlePlayerMove(Session session, JSONObject moveData) { // 处理玩家移动逻辑 // 广播给所有玩家 broadcastGameUpdate(moveData); } private void handleChatMessage(Session session, JSONObject chatData) { // 广播聊天消息 JSONObject broadcastMsg new JSONObject(); broadcastMsg.put(type, chat); broadcastMsg.put(sender, session.getId()); broadcastMsg.put(message, chatData.getString(message)); broadcastMsg.put(timestamp, System.currentTimeMillis()); broadcast(broadcastMsg.toString()); } private void broadcast(String message) { synchronized (sessions) { for (Session s : sessions) { if (s.isOpen()) { try { s.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } } } } }配置文件application.yml 配置spring: websocket: # WebSocket 配置 enabled: true server: # 服务器配置 port: 8080 servlet: context-path: /api # 自定义配置 websocket: max-sessions: 1000 heartbeat-interval: 30000 max-message-size: 128KB心跳检测和连接管理Component public class WebSocketHeartbeat { Autowired private SimpMessagingTemplate messagingTemplate; private ScheduledExecutorService scheduler Executors.newScheduledThreadPool(1); PostConstruct public void init() { // 每30秒发送一次心跳 scheduler.scheduleAtFixedRate(() - { try { messagingTemplate.convertAndSend(/topic/heartbeat, Map.of(timestamp, System.currentTimeMillis(), type, heartbeat)); } catch (Exception e) { e.printStackTrace(); } }, 0, 30, TimeUnit.SECONDS); } PreDestroy public void destroy() { scheduler.shutdown(); } }消息编码器/解码器// 自定义消息编解码器 Component public class ChatMessageConverter implements MessageConverter { Override public Message? toMessage(Object payload, MessageHeaders headers) { if (payload instanceof ChatMessage) { ChatMessage msg (ChatMessage) payload; byte[] bytes serializeMessage(msg); return MessageBuilder.createMessage(bytes, headers); } return null; } Override public Object fromMessage(Message? message, Class? targetClass) { if (targetClass ChatMessage.class) { byte[] bytes (byte[]) message.getPayload(); return deserializeMessage(bytes); } return null; } private byte[] serializeMessage(ChatMessage message) { try { return new ObjectMapper().writeValueAsBytes(message); } catch (Exception e) { throw new RuntimeException(序列化失败, e); } } private ChatMessage deserializeMessage(byte[] bytes) { try { return new ObjectMapper().readValue(bytes, ChatMessage.class); } catch (Exception e) { throw new RuntimeException(反序列化失败, e); }

相关新闻

LX Music Desktop:一站式开源音乐播放器的革命性体验

LX Music Desktop:一站式开源音乐播放器的革命性体验

LX Music Desktop:一站式开源音乐播放器的革命性体验 【免费下载链接】lx-music-desktop 一个基于 Electron 的音乐软件 项目地址: https://gitcode.com/GitHub_Trending/lx/lx-music-desktop 在数字音乐时代,我们常常面临一个困境:音…

2026/7/1 12:39:48阅读更多 →
SLO2016与PIC32MZ2048EFM144在工业通信中的高效组合

SLO2016与PIC32MZ2048EFM144在工业通信中的高效组合

1. SLO2016与PIC32MZ2048EFM144的黄金组合解析 在工业通信和嵌入式系统领域,数据传输的可靠性与实时性始终是开发者面临的核心挑战。SLO2016作为一款专业级串行通信协议芯片,与Microchip公司推出的PIC32MZ2048EFM144高性能微控制器相结合,构成…

2026/7/1 12:39:48阅读更多 →
Linux/Windows双平台VMware Tools安装失败全场景排查手册(含ESXi 8.0+兼容性避坑清单)

Linux/Windows双平台VMware Tools安装失败全场景排查手册(含ESXi 8.0+兼容性避坑清单)

更多请点击: https://kaifayun.com 第一章:VMware Tools安装失败的典型现象与诊断定位 VMware Tools 是提升虚拟机性能与集成度的关键组件,但其安装过程常因环境差异而失败。常见现象包括:安装程序无响应、进度条卡在“正在配置驱…

2026/7/1 12:39:48阅读更多 →
逻辑严谨吗?8款一键生成论文工具排名,毕业论文轻松搞定!

逻辑严谨吗?8款一键生成论文工具排名,毕业论文轻松搞定!

论文选题卡壳怎么办?文献综述写不出逻辑?格式调整反复修改还出错? 别担心!AI论文写作工具正在重新定义学术写作的效率。本文将基于内容质量、文献支持、格式规范和查重表现四大核心维度,实测8款热门AI论文生成工具&am…

2026/7/1 13:55:01阅读更多 →
低成本高精度IMU运动测量系统设计与实现

低成本高精度IMU运动测量系统设计与实现

1. 项目背景与核心需求在工业自动化、机器人导航和运动控制领域,精确的惯性运动测量一直是技术难点。传统方案要么成本高昂,要么在动态环境下稳定性不足。这次我们要解决的问题,是如何用相对经济的方案实现专业级的运动测量精度。我选择了TDK…

2026/7/1 13:55:01阅读更多 →
大模型推理部署实战:从 GPU 显存管理到高并发服务化的全链路设计

大模型推理部署实战:从 GPU 显存管理到高并发服务化的全链路设计

大模型推理部署实战:从 GPU 显存管理到高并发服务化的全链路设计一、Token 吞吐与显存瓶颈:LLM 部署的工程困境 大模型推理部署的核心矛盾在于:GPU 显存是有限的,而模型参数和 KV Cache 的显存需求几乎是无上限的。一个 70B 参数的…

2026/7/1 13:55:01阅读更多 →
Oracle WHERE条件执行顺序误区、REGEXP正则与LIKE索引性能对比(生产实战)

Oracle WHERE条件执行顺序误区、REGEXP正则与LIKE索引性能对比(生产实战)

前言 在Oracle开发与调优中,长期存在两个广为流传的错误经验: 1、WHERE条件从左到右执行,必须把精准条件写在前面才能提速; 2、正则写 ^ 前缀就能像 LIKE XX% 一样走索引。 很多开发在大数据量表查询中,乱用 REGEXP_LIKE、纠结 WHERE 条件书写顺序,最终导致SQL卡顿、…

2026/7/1 13:55:01阅读更多 →
极简架构设计:微服务拆分的“少即是多“方法论

极简架构设计:微服务拆分的“少即是多“方法论

极简架构设计:微服务拆分的"少即是多"方法论一、过度拆分的陷阱:当微服务变成微地狱 微服务架构的推广中存在一个普遍误区:拆得越细越好。一个日活不到 1 万的应用,被拆成 15 个微服务,每个服务独立部署、独…

2026/7/1 13:55:01阅读更多 →
STM32与74HC32实现低成本矩阵键盘方案

STM32与74HC32实现低成本矩阵键盘方案

1. 项目背景与核心需求在嵌入式系统开发中,如何用最精简的硬件资源实现多功能控制一直是个经典课题。这次我尝试用74HC32四或门芯片配合STM32F767ZG开发板,搭建了一个2x2矩阵键盘系统,实现了四个独立功能的切换管理。这种方案特别适合需要低成…

2026/7/1 13:50:00阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/1 4:42:14阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/1 5:19:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →