websocket服务器端连接标识通常用业务层唯一标识,如url路径参数(/ws/{userid})、查询参数(?token=abc123)、首次握手消息或jwt解析结果,而非不可控的tcp socket id或不稳定的session id。

WebSocket 服务器端要准确识别、路由和管理每个客户端连接,核心在于建立并维护“连接标识 ↔ 实际会话”的映射关系。这个映射不是可选的附加功能,而是实现精准消息推送、状态跟踪、权限控制和资源清理的前提。
连接标识通常用什么?
服务端一般不直接依赖底层 TCP socket ID(操作系统生成、不可控、不跨进程),而是主动设计业务层唯一标识,常见方式包括:
-
URL 路径参数:如
/ws/{userId}或/ws/{deviceId},在@ServerEndpoint("/ws/{sid}")中提取sid作为 key -
查询参数:连接时携带
?token=abc123,服务端解析后校验并提取用户 ID 或设备 ID -
首次握手消息:连接建立后,客户端立即发送认证消息(如 JSON 格式
{ "type": "auth", "uid": "u1001" }),服务端验证通过后才正式注册 -
JWT 解析:若 WebSocket 升级请求带
Authorization头,服务端可在onOpen前拦截并解析 token 获取身份信息
⚠️ 注意:仅靠
session.getId()或session.getBasicRemote().getSendHandler()等原生方法获取的 ID 不稳定、不具业务语义,也不便于集群共享,不应作为业务主键使用。
如何安全存储映射关系?
推荐使用线程安全、支持高并发的容器,例如:
-
ConcurrentHashMap<string session></string>:key 是业务标识(如 userId),value 是javax.websocket.Session - 配合
CopyOnWriteArrayList<session></session>存储群组会话(如房间内所有在线用户) - 若需跨节点共享(如多实例部署),应外接 Redis,以
userId为 key,存sessionId或序列化后的会话元数据
示例代码片段(Spring Boot + JSR-356):
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
private static final Map<string session> SESSION_MAP = new ConcurrentHashMap();
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
// 校验 userId 合法性(非空、格式、权限等)
if (isValidUser(userId)) {
SESSION_MAP.put(userId, session);
System.out.println("用户 " + userId + " 已上线");
} else {
session.close(); // 拒绝非法连接
}
}
@OnClose
public void onClose(@PathParam("userId") String userId) {
SESSION_MAP.remove(userId);
System.out.println("用户 " + userId + " 已下线");
}</string>
映射关系必须配合生命周期管理
光存进去不够,还要确保一致性——连接断开、异常关闭、超时踢出时,映射必须同步清理:
-
@OnClose:正常关闭时移除 -
@OnError:发生 I/O 异常或编码错误时,先尝试获取关联 userId(可通过session.getUserProperties()提前存入),再清理 - 主动踢人:服务端调用
session.close()后,必须手动从 map 中 remove,否则内存泄漏 + 误推消息 - 心跳超时:单独启动定时任务扫描
lastHeartbeatTime,对超时连接主动 close 并清理 map
为什么不能只靠 Session 对象本身?
因为 Session 是临时对象,生命周期绑定于单次连接;它不携带业务上下文,也无法跨服务实例传递。一旦你希望做到:
- 给“张三”发消息,而不是给“某个已断开的 session”发
- 查看“李四当前是否在线”
- 在用户重连时恢复未读消息
- 多台服务器协同广播
就必须把业务标识(如 userId)作为主键,与 Session 显式绑定,并全程守护其一致性。
不复杂但容易忽略










