【经验分享】SpringBoot集成WebSocket开发-03 使用WebSocketSession为每个对话存储独立信息

devtools/2025/3/14 12:36:53/

WebSocketSession为每个对话存储独立信息

要在每个WebSocket会话中存储独立的信息,比如用户信息、对话唯一ID等,可以通过以下几种方式来实现:

1. 使用WebSocketSession存储会话级别的属性

WebSocketSession对象提供了一个getAttributes()方法,可以用来为每个WebSocket会话存储自定义信息。我们可以将用户信息、会话ID等存储在这个属性中,确保每个会话都有独立的数据。

2. 管理WebSocket会话

我们可以在WebSocket处理器中跟踪每个WebSocket会话,并在会话关闭时清理数据。

示例实现

下面是一个完整的示例,展示如何为每个WebSocket会话存储独立的信息:

1. 修改 MyWebSocketHandler,使用 WebSocketSession 存储会话信息
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.TextMessage;import java.util.Map;
import java.util.UUID;@Component
public class MyWebSocketHandler extends TextWebSocketHandler {private final MyService myService;// 构造器注入MyServicepublic MyWebSocketHandler(MyService myService) {this.myService = myService;}// 处理消息@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) {// 从session获取对话唯一ID和用户信息String conversationId = (String) session.getAttributes().get("conversationId");String userName = (String) session.getAttributes().get("userName");// 可以将这些信息传给service进行处理String response = myService.processMessage(message.getPayload(), conversationId, userName);try {// 发送处理后的消息session.sendMessage(new TextMessage(response));} catch (Exception e) {e.printStackTrace();}}// 在会话建立时设置会话信息@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 设置对话唯一ID和用户信息String conversationId = UUID.randomUUID().toString(); // 生成唯一的对话IDString userName = "Guest-" + UUID.randomUUID().toString(); // 假设用户名是随机生成的,实际中可以通过认证来获取// 将对话ID和用户名存入会话属性session.getAttributes().put("conversationId", conversationId);session.getAttributes().put("userName", userName);System.out.println("Connection established for user: " + userName + " with conversation ID: " + conversationId);}// 在会话关闭时清理资源@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {// 清理资源session.getAttributes().clear();System.out.println("Connection closed for session: " + session.getId());}
}
2. 修改 MyService,接受对话ID和用户名参数

MyService类需要处理来自不同会话的消息,并根据不同的对话ID和用户信息来做出响应。

import org.springframework.stereotype.Service;@Service
public class MyService {public String processMessage(String message, String conversationId, String userName) {// 这里根据对话ID和用户名处理消息// 你可以通过conversationId来查找历史对话记录,或者根据用户名做不同的响应return "用户: " + userName + " 在对话: " + conversationId + " 中发送的消息: " + message;}
}
3. WebSocket 配置类

WebSocketConfig类不需要做修改,只需要保持WebSocket的基本配置即可。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {private final MyWebSocketHandler myWebSocketHandler;// 注入自定义的 WebSocketHandlerpublic WebSocketConfig(MyWebSocketHandler myWebSocketHandler) {this.myWebSocketHandler = myWebSocketHandler;}@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(myWebSocketHandler, "/ws").setAllowedOrigins("*");}
}
4. 启动类

与之前一样,我们创建一个Spring Boot启动类来启动应用。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class WebSocketApplication {public static void main(String[] args) {SpringApplication.run(WebSocketApplication.class, args);}
}

5. 说明

  • MyWebSocketHandler 中,使用了 WebSocketSession 对象的 getAttributes() 来存储会话级别的属性,如对话ID和用户名等。这些信息在WebSocket会话建立时被初始化,并且在后续的消息处理中被引用。
  • 使用 UUID.randomUUID().toString() 来生成一个唯一的对话ID,每个连接会有一个独立的对话ID。实际应用中,你可以通过身份认证来为用户生成一个真实的用户名。
  • 每个WebSocket连接都是独立的,WebSocketSession 会自动维护各自独立的状态,所以每个连接可以保存自己的对话ID和用户信息。
  • 在会话关闭时,通过 afterConnectionClosed() 方法清理会话信息,确保没有残留的数据。

6. 进一步扩展

  • 认证: 如果你需要根据用户身份来设置用户信息,可以在 afterConnectionEstablished() 方法中进行认证(比如使用JWT令牌来识别用户)。
  • 历史消息: 你可以在 MyService 中扩展,利用 conversationId 来查找并返回该对话的历史消息。
  • 广播消息: 如果你需要广播消息给所有用户,可以使用一个全局的WebSocket会话管理器来存储每个会话,并根据需求发送消息。

这样,每个WebSocket会话就能够独立地存储和访问用户信息、对话ID等数据,同时能够根据不同的会话做出不同的响应。

客户端主动发送消息

向多个客户端广播消息

如果你希望向所有连接的客户端广播消息,而不仅仅是某个单独的客户端,你需要维护一个所有WebSocket会话的列表。这通常可以通过一个简单的集合来实现,比如使用Set<WebSocketSession>来存储所有活跃的WebSocket会话。

示例:广播消息给所有连接的客户端
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;@Component
public class MyWebSocketHandler extends TextWebSocketHandler {private final Set<WebSocketSession> sessions = new CopyOnWriteArraySet<>(); // 线程安全的集合,保存所有会话@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 将新的会话添加到集合中sessions.add(session);String userId = (String) session.getAttributes().get("userId");sendMessageToClient(session, "Hello, " + userId + "! You are connected.");}@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 处理接收到的消息后,可以向所有客户端广播broadcastMessage("Broadcast message: " + message.getPayload());}@Overridepublic void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) throws Exception {// 移除关闭的会话sessions.remove(session);}// 向所有连接的客户端广播消息private void broadcastMessage(String message) throws Exception {TextMessage textMessage = new TextMessage(message); // 创建TextMessage对象for (WebSocketSession session : sessions) {session.sendMessage(textMessage); // 向所有客户端发送消息}}// 向单个客户端发送消息private void sendMessageToClient(WebSocketSession session, String message) throws Exception {TextMessage textMessage = new TextMessage(message);session.sendMessage(textMessage); // 发送消息}
}

关键点:

  • sessions:这个集合存储了所有活跃的WebSocket会话,每个客户端的连接都会被保存到这个集合中。

  • broadcastMessage:这个方法遍历所有活跃的会话,并向每个会话发送消息,实现了广播消息功能。

  • afterConnectionEstablished:在每个客户端连接时,将其WebSocket会话对象添加到sessions集合中。

  • afterConnectionClosed:在WebSocket连接关闭时,移除对应的会话对象,确保集合中只保留当前活跃的会话。

  • 可以进一步优化为线程安全的map进行存储session信息例如

       /*** 使用ConcurrentHashMap来存储WebSocket会话*/private final ConcurrentHashMap<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
    

http://www.ppmy.cn/devtools/167025.html

相关文章

git submodule管理的仓库怎么删除子仓库

删除 Git 子模块需要执行一系列步骤&#xff0c;以确保从项目中彻底移除子模块及其相关配置。以下是详细的步骤&#xff1a; 1. 取消初始化子模块 运行以下命令以取消子模块的初始化&#xff0c;这会从 .git/config 文件中移除子模块的配置&#xff1a; git submodule deini…

Yashan DB 应用开发

一、YashanDB支持的驱动 YashanDB支持多种驱动&#xff0c;包括JDBC、C、Python、ADO.NET、ODBC等。以下是详细介绍&#xff1a; 1.JDBC驱动 • 定义&#xff1a;JDBC是Java连接数据库的技术统称。Java语言只提供规范&#xff08;接口&#xff09;&#xff0c;规定数据库操作…

Visual Studio关闭警告

Visual Studio关闭警告信息 下面的例子基于VS2019, 其他版本类似。 新建项目之后&#xff0c;在里面写一些会产生警告的代码&#xff0c;如&#xff1a; char c 666; 编译器会警告&#xff1a; 除了警告&#xff0c;运行结果也会有问题。可以考虑把警告信息关掉&#xff1a; …

蓝桥与力扣刷题(蓝桥 螺旋折线)

题目&#xff1a;如下图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 (X,Y)&#xff0c;我们定义它到原点的距离 dis(X,Y) 是从原点到 (X,Y) 的螺旋折线段的长度。 例如 dis(0,1)3,dis(−2,−1)9。 给出整点坐标 (X,Y)&#xff0c;你能计算出 dis(X,Y) 吗&#xff1…

【医院绩效管理专题】8.医院绩效数据的收集与整理方法:洞察现状,引领未来

医院成本核算、绩效管理、运营统计、内部控制、管理会计专题索引 一、引言 在当今医疗行业竞争日益激烈的背景下,医院绩效管理已成为提升医疗服务质量、优化运营效率、增强综合竞争力的关键因素。而绩效数据的收集与整理作为绩效管理的基础环节,其科学性、准确性和完整性直…

ResNet改进(4):添加Inception 结构

1.Inception 结构 Inception 结构是由 Google 提出的经典卷积神经网络架构&#xff0c;首次出现在 2014 年的论文《Going Deeper with Convolutions》中&#xff0c;并在 ImageNet 图像分类竞赛中取得了优异成绩。Inception 结构的目标是通过多尺度卷积和高效计算来提升网络性…

《苍穹外卖》SpringBoot后端开发项目核心知识点与常见问题整理(DAY1 to DAY3)

目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功&#xff1a; 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具&#xff1a;Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…

e2studio开发RA4L1(1)---开发板测试

e2studio开发RA4L1.1-- 开发板测试 概述视频教学样品申请产品特性参考程序源码下载硬件准备新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置GPIO口配置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置主程序 概述 RA4L1 评估套件可以使用户能够无…