基于websocket聊天室的基本点
一、核心注解与变量说明
1. 类注解
@ServerEndpoint(value="/chat/{roomId}/{nickname}", configurator = GetHttpSessionConfig.class)
标记这是一个 WebSocket 端点,客户端通过ws://服务器地址/chat/房间ID/用户名
连接。configurator
用于获取 HTTP 会话信息(可选)。@Component
:将该类注册为 Spring 组件,使其被 Spring 管理。@Slf4j
:Lombok 注解,提供日志打印功能(log.info()
等)。
2. 静态变量(全局共享)
onlineCount
:原子整数,线程安全地记录当前在线用户总数。userToRoomMap
:ConcurrentHashMap
,记录「用户会话 ID → 房间 ID」的映射(用于快速查找用户所在房间)。roomToUsersMap
:ConcurrentHashMap
,记录「房间 ID → 房间内用户集合」的映射(用于管理房间内的用户)。
3. 实例变量(每个连接独有)
session
:当前用户的 WebSocket 会话对象(用于收发消息)。roomId
:当前用户所在的房间 ID。nickname
:当前用户的昵称。gson
:Gson 实例,用于 JSON 格式的消息转换(前后端消息通过 JSON 传递)。
二、核心方法详解
1. 连接建立:@OnOpen
注解方法
java
运行
@OnOpen
public void onOpen(Session session, @PathParam("roomId") String roomId, @PathParam("nickname") String nickname) throws IOException
- 触发时机:客户端首次连接到 WebSocket 端点时调用。
- 功能:
- 初始化当前连接的会话(
session
)、房间 ID(roomId
)、用户昵称(nickname
)。 - 在线用户数 +1(
onlineCount.incrementAndGet()
)。 - 将用户加入房间:如果房间不存在则创建(
roomToUsersMap.computeIfAbsent
),并将用户信息(User
对象)存入房间。 - 记录用户与房间的映射(
userToRoomMap.put
)。 - 发送欢迎消息给当前用户(
sendMessage
)。 - 广播 “用户加入” 的系统消息给房间内其他用户(
broadcastSystemMessage
)。 - 广播更新房间内的在线用户列表(
broadcastUserList
)。
- 初始化当前连接的会话(
2. 接收消息:@OnMessage
注解方法
java
运行
@OnMessage
public void onMessage(String message, Session session)
- 触发时机:收到客户端发送的消息时调用(消息格式为 JSON 字符串)。
- 功能:
- 解析 JSON 消息为
Map
(parseMessage
方法),获取消息类型(type
)。 - 根据消息类型分发给不同的处理方法:
type="msg"
:文本消息 → 调用handleTextMessage
。type="img"
:图片消息 → 调用handleImageMessage
。type="init"
:初始化房间 → 调用handleRoomJoin
。type="ping"
:心跳检测 → 忽略。
- 解析 JSON 消息为
3. 连接关闭:@OnClose
注解方法
java
运行
@OnClose
public void onClose(Session session)
- 触发时机:客户端断开 WebSocket 连接时调用。
- 功能:
- 调用
handleUserExit
处理用户退出逻辑。 - 打印日志记录用户断开连接。
- 调用
4. 消息处理核心方法
(1)处理图片消息:handleImageMessage
java
运行
private void handleImageMessage(Map<String, String> msgMap)
- 功能:处理客户端发送的图片消息(本质是图片 URL)。
- 通过
getUser(session)
获取发送者信息。 - 构建响应消息:包含类型(
type="img"
)、图片 URL(msg
字段,来自客户端的content
)、发送者昵称(sendUser
)。 - 调用
broadcastMessage
将图片消息广播给房间内所有用户。
- 通过
(2)处理文本消息:handleTextMessage
java
运行
private void handleTextMessage(Map<String, String> msgMap)
- 功能:处理客户端发送的文本消息。
- 获取发送者信息,构建响应消息:类型(
type="msg"
)、文本内容(msg
)、发送者昵称(sendUser
)。 - 广播消息给房间内所有用户(支持排除被屏蔽的用户)。
- 获取发送者信息,构建响应消息:类型(
(3)处理用户加入房间:handleRoomJoin
java
运行
private void handleRoomJoin(Map<String, String> msgMap)
- 功能:当客户端主动发送 “加入房间” 请求时调用(补充
onOpen
逻辑)。- 从消息中获取房间 ID 和昵称,创建用户对象并加入房间。
- 广播 “用户加入” 的系统消息。
(4)处理用户退出:handleUserExit
java
运行
private void handleUserExit()
- 功能:用户断开连接时,清理用户信息并更新房间状态。
- 从房间中移除用户,在线用户数 -1。
- 广播 “用户离开” 的系统消息,更新在线用户列表。
- 如果房间为空(用户全部离开),删除房间(
roomToUsersMap.remove
)。
5. 消息广播方法
(1)广播系统消息:broadcastSystemMessage
java
运行
private void broadcastSystemMessage(CopyOnWriteArraySet<User> users, String message)
- 功能:向房间内所有用户发送系统消息(如 “用户加入 / 离开”)。
- 构建系统消息(
type="system"
,发送者为 “系统消息”)。 - 遍历房间内用户,仅向会话未关闭的用户发送消息。
- 构建系统消息(
(2)广播用户列表:broadcastUserList
java
运行
private void broadcastUserList(CopyOnWriteArraySet<User> users)
- 功能:当房间内用户变化时(加入 / 离开),向所有用户广播最新的在线用户列表。
- 构建用户列表消息(
type="user-list"
),包含每个用户的 ID 和昵称。 - 通过
broadcastMessage
广播给房间内用户。
- 构建用户列表消息(
(3)通用广播方法:broadcastMessage
java
运行
private void broadcastMessage(CopyOnWriteArraySet<User> users, String message, String excludeUsers)
- 功能:向房间内用户广播消息,支持排除指定用户(如被屏蔽的用户)。
- 过滤条件:仅向会话打开的用户发送,且不在排除列表中。
- 遍历用户并发送消息(
user.getWebSocket().sendMessage
)。
6. 辅助方法
sendMessage
:向当前会话(单个用户)发送消息(需检查会话是否打开)。parseMessage
:将 JSON 字符串解析为Map
(方便获取消息字段)。getRoomUsers
:根据会话获取用户所在房间的所有用户。getUser
:根据会话查找对应的用户对象(通过会话 ID 匹配)。
三、整体流程总结
- 用户连接:客户端通过
ws://地址/chat/房间ID/昵称
连接,onOpen
方法初始化并加入房间,广播用户加入。 - 发送消息:客户端发送文本 / 图片消息,
onMessage
解析类型后调用对应处理方法,构建响应并广播给房间内所有用户。 - 用户退出:客户端断开连接,
onClose
触发handleUserExit
,清理用户信息,广播用户离开,更新在线列表。
通过线程安全的集合(ConcurrentHashMap
、CopyOnWriteArraySet
)保证多用户并发操作时的数据一致性,实现了一个支持多房间、多用户实时聊天的功能。