websocket优雅断开需主动触发关闭帧(如close(1000,"page unloaded")),配合服务端完成rfc 6455四次挥手,清理定时器、取消pending操作、置socket为null,并依据event.code与wasclean区分正常关闭与异常中断,启动退避重连。

WebSocket 连接的“优雅断开”不是简单调用 close() 就完事,而是要在连接真正关闭前完成状态清理、资源释放、事件通知,并避免二次关闭或并发冲突。核心在于主动控制、分层解耦、状态可溯。
主动关闭必须走阻塞或带确认流程
调用 session.close() 是异步的,可能在底层 TCP 还未真正断开时就返回,导致后续操作(如清 session 缓存)与实际连接状态不一致。更稳妥的做法是:
- 优先使用
session.closeBlocking()(JSR-356 标准支持),它会等待关闭帧发送并收到对端响应,确保握手完成 - 若不可用,手动加超时等待:调用
close()后,启动一个短时(如 2 秒)定时任务,检查session.isOpen() == false再执行后续清理 - 关闭前先置状态:设置本地连接状态为
CLOSING,防止重连线程或心跳任务在此期间误操作
清理动作必须与生命周期回调解耦
@OnClose 回调里只做轻量状态变更,所有释放逻辑应外移:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 从全局
ConcurrentHashMap<string session></string>中移除当前 session - 取消该 session 关联的定时心跳任务(如
ScheduledFuture.cancel(true)) - 关闭专属资源:比如为该连接单独创建的数据库连接、缓存订阅、消息队列消费者等
- 切勿在
@OnClose中调用session.getBasicRemote().sendText()—— 此时 session 已不可用,会抛IllegalStateException
区分关闭原因,避免误判静默断连
WebSocket 的 @OnClose 会传入 CloseReason,但很多中间设备(如 Nginx、云 LB)静默回收连接时,根本不会发关闭帧,@OnClose 不触发,只能靠心跳失败或 @OnError 中的 EOFException 推断:
- 正常关闭:code 为 1000,reason 可读,说明是双方协商断开
- 服务端强制关闭:code 常见 1001(going away)、1002(protocol error),需结合日志判断是否运维行为
- 静默断连:无
@OnClose,但心跳定时任务超时失败,或@OnError收到java.io.EOFException,此时应按异常断连处理,启动重连流程
关闭后状态归零,防止残留引用
一次完整关闭后,客户端或服务端对象不能“半残”存活:
- 将 session 引用设为
null或从持有容器中彻底移除,避免内存泄漏 - 重置重连计数器(如
reconnectCount = 0),否则下次连接可能直接跳过重试 - 清除与该连接绑定的 ThreadLocal 变量、MDC 日志上下文、用户认证凭证等
- 若使用 Netty 底层,确保 ChannelPipeline 中相关 handler 已 remove,避免后续事件误路由
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










