gorilla/websocket服务端主动关闭需调用conn.Close(),它自动发送合规关闭帧并等待响应;若需自定义code和reason,须先用websocket.FormatCloseMessage构造payload再WriteMessage,最后调conn.Close(),不可仅WriteMessage后return。

服务端主动断开 WebSocket 连接,核心是发送符合 RFC 6455 的关闭帧,不是直接关 TCP;传错 code 或乱调 close() 会卡在 CLOSING、触发 1006 异常关闭,甚至让客户端收不到 reason。
gorilla/websocket 中如何正确调用 close() 发送关闭帧
Go 生态最常用的是 gorilla/websocket,它的 conn.Close() 是阻塞式发送关闭帧并等待对端响应(最多 30 秒),不是立即释放连接。
-
conn.Close()内部会自动调用conn.WriteMessage(websocket.CloseMessage, ...),你不需要手动拼帧 - 若想带自定义状态码和原因,必须用
websocket.FormatCloseMessage(code, reason)构造 payload,再WriteMessage,最后conn.Close()—— 不能只写不关 - 常见错误:直接
conn.WriteMessage(websocket.CloseMessage, []byte("bye"))后就 return,这会导致客户端收到不合规帧(缺 2 字节 code),触发1002协议错误 - 合法
code范围:仅1000(正常)、1001(离开)、1002~1015(协议/编码错误),或4000~4999(应用自定义);3000、5000会静默失败或被中间件拦截
FastAPI WebSocket 中 send() 关闭帧的替代方案
FastAPI 的 WebSocket 对象没有原生 close() 方法,也不能直接 send() 原始字节帧。它依赖底层 ASGI 生命周期管理,所以“主动断开”要靠异常 + 状态控制。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 推荐方式:在处理逻辑中
raise WebSocketDisconnect(code=1000, reason="logged out"),ASGI 会自动发送关闭帧并清理 - 避免手动
await websocket.close()(该方法不存在)或await websocket.send_text('{"type":"close"}')—— 这只是业务消息,不会触发协议级关闭 - 若需延迟关闭(如等消息发完),用
try/except捕获WebSocketDisconnect,并在finally块里做资源清理,但不要试图“重开”已断开的websocket实例 - 注意:FastAPI 默认不设心跳,长连接空闲超时(如 Nginx 的
proxy_read_timeout)会由代理发 RST,此时服务端收不到on_close,只能靠客户端上报或定时 ping
Node.js ws 库中 server.close() 与 client.close() 的区别
ws 库里两个 close() 完全不同:一个是服务器实例的全局关闭(停监听),一个是单个连接的协议关闭(发帧)。
-
wss.close():停止接受新连接,已建立的连接不受影响;调用后不能再wss.handleUpgrade -
ws.close(code, reason):对单个客户端连接发送关闭帧;code必须是数字(非字符串),reason长度 ≤123 字节 UTF-8(中文字符占 3 字节,123 字节 ≈ 41 个汉字) - 典型误用:
ws.close("1000", "done")→ 报错the code must be a number;ws.close(1000, "✅ OK")→ ✅ 合法,但 emoji 占多字节,需提前Buffer.byteLength(reason, 'utf8') 校验 - 服务端调用
ws.close()后,客户端onclose事件的event.code和event.reason才能被正确读取;否则可能 fallback 到1006
微信小程序 wx.closeSocket 的时序陷阱
小程序的 wx.closeSocket 不是 JS 原生 API,而是异步桥接调用,readyState 检查必须在 wx.onSocketOpen 回调内做,否则调用无效。
- 错误写法:
wx.connectSocket({ url }); wx.closeSocket();→ 静默失败,连接仍在 - 正确写法:必须等
wx.onSocketOpen触发后,再调wx.closeSocket({ code: 1000, reason: "user logout" }) -
code若非法(如负数、"1000"字符串、超过 65535),fail回调会返回invalid close code;reason超长则截断,不报错但服务端收不到完整内容 - 务必配对使用
success和fail:在success里清除setInterval、注销wx.onSocketMessage,否则内存泄漏风险高
真正难的不是“怎么关”,而是关之前确认对端还活着、关之后清理干净、关失败时有降级路径。RFC 6455 的关闭帧不是命令,是一次协商;服务端发了,不代表客户端收到了,关键状态变更一定要走 HTTP 双写兜底。

















