1000表示连接完成任务后有序预期关闭,1001表示端点即将不可用;二者均走关闭帧流程,但服务端对1001通常立即释放资源,而1000可能支持复用或快速重连。
1000 和 1001 都是主动关闭,但发起方和语义完全不同
1000 normal_closure 表示连接已完成既定任务后被双方之一**有序、预期地关闭**,不暗示任何异常或状态变更;1001 going_away 则明确表示**端点即将不可用**——比如服务器正在滚动更新、浏览器标签页正要跳转或关闭。它们都走标准关闭帧流程,但服务端收到 1001 时通常会立即释放资源,而 1000 可能只是本次会话结束,后续还可能复用连接或快速重连。
前端如何区分是页面卸载导致的 1001 还是业务逻辑触发的 1000
不能只依赖 onclose 事件里的 event.code,因为浏览器在页面刷新/关闭前可能来不及发出带码的关闭帧,实际收到的常是 1006(异常)或根本没触发 onclose。可靠做法是结合生命周期钩子:
- 监听
beforeunload或pagehide,提前调用socket.close(1001, "Page navigation") - 在路由切换(如 React Router 的
useEffectcleanup 或 Vue 的onBeforeUnmount)中显式 close 并传入 1001 - 业务逻辑关闭一律用
socket.close(1000, "Task completed"),避免混用
服务端收到 1001 后该不该立刻清理用户状态
取决于你的状态模型。如果用户登录态、房间成员关系等依赖 WebSocket 连接活跃性,那么 1001 是一个强信号:客户端大概率不会再回来,可以安全清除;但如果用了 JWT + 独立 session 管理,且允许短时断线重连,则应延迟清理(例如加个 30 秒 grace period),否则用户切个标签页就掉线。
关键点在于:1001 不等于“永久下线”,它只代表“这个 socket 实例即将失效”。是否影响业务状态,得由你定义。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
为什么日志里看到大量 1000 却怀疑有问题
1000 本身无害,但高频出现往往暴露设计缺陷:
- 每次发一条消息就
close()再重连 → 应改为长连接 + 请求 ID 匹配 - 前端路由组件反复挂载/销毁,未复用
WebSocket实例 → 改为全局单例或 Context 管理 - 服务端设置了过短的 idle timeout(如 5 秒),空闲即关 → 检查反向代理(Nginx/ALB)和框架配置(如 Spring Boot 的
server.tomcat.connection-timeout)
真正难排查的是那种“看起来是 1000,但其实是服务端静默 kill”的情况——比如负载均衡器在连接空闲 60 秒后直接 RST,客户端收到的却是 1000,因为它伪造了关闭帧。这时候必须比对客户端日志和服务端网络层日志(如 tcpdump)才能确认。










