SuperAPI AI 工具集

websocket-session-handshake-lifecycle

编程与开发

查看项目

更新于 10-17 16:16

技能介绍

用途

本文档讨论的是 Kotlin 协程环境中 WebSocket 客户端的会话生命周期:一个连接从建立、握手、进入就绪状态,到最终关闭的完整顺序,以及每一步容易出错的地方。核心观点是:WebSocket 没有请求—响应结构,第一条消息的应答会通过同一个读取流返回,因此握手能否完成取决于读取循环是否已经先跑起来。

主要能力与要点

握手顺序。 代码骨架显示:先创建 webSocketSession,再 launch 读取协程,然后发送 HELLO 与本端能力,用 CompletableDeferred 把"读循环里收到一帧"桥接回"等待握手的调用方",并用 withTimeoutOrNull 限定握手时长。读取协程必须排在发送之前,否则对端应答无人接收,表现为 socket 正常打开但随后超时。

就绪的定义。 不能把 session != null 当作就绪,那等于把"连接被接受"误认为"握手完成"。正确做法是要求 socket 活跃且握手完成,超时则抛出异常,让失败进入重连路径,而不是让协程挂死。

关闭帧是挂起调用。 被取消的任务不会发出关闭帧。主动断开时若用普通 finally,关闭会在第一个挂起点被中止,对端要等到读取超时才回收会话。需要在关闭外层包 withContext(NonCancellable)。

事件缓冲。 应使用默认的挂起策略并给足容量,不要用 BufferOverflow.DROP_*;丢失一个协议帧会造成静默失步。丢弃策略适合 UI 通知,不适合状态机消费的流。

读取循环中的错误处理。 Kotlin 不允许在 lambda 内 break/continue,getOrElse { continue } 无法编译;应先把结果解包为可空值,再在循环体内分支。对于类型不符的帧应显式跳过,而不是交给解码器。

恢复令牌。 令牌在普通消息中随帧到达,应在帧处理器中捕获,下次连接时重放,并在对端移除、放弃重连、用户主动离开三条路径上清除。用户主动离开时若不清除,下次连接会错误地恢复旧会话,重放过期令牌会得到错误而非会话。

发送返回值。 send 返回 false 表示未发出,且不会自动重发——重连只重放令牌,不重放流量。调用方必须处理 false,清理乐观状态或上报。

句柄清理时机。 应在关闭的同一个 finally 中清空 session 与 handshake,而不是等到下次连接开始时;否则就绪判断会基于陈旧句柄,对已关闭会话反复发送并得到一连串 "not sent",却看不到任何断开。

心跳循环。 ping 循环同时承担保活与时钟校准采样两个职责,间隔要明显小于对端的读取超时;发送失败应直接返回,而不是在死连接上继续循环。

使用方式

文档给出三条 grep 审计命令,分别用于清点所有持有 socket 的客户端、列出所有挂起取消的位置、查看缓冲区声明。建议把第一份清单与第二份清单交叉比对:未出现在 NonCancellable 列表中的客户端,其关闭只可能是在没被取消的路径上偶然成功。缓冲区检查中若在协议流旁看到 DROP_*,即为失步风险。

验证需覆盖两条路径:其一,连接一个只接受连接但从不回应的对端,超时必须只触发一次、状态不得报告就绪、重连路径必须接管;其二,在流量传输中主动断开,从对端侧确认收到关闭帧,而非会话一直挂到读取超时。

适用场景

适用于用 Kotlin 协程实现 WebSocket 客户端、需要自定义应用层握手、保活与重连逻辑的工程场景,尤其是多人协作、代码会被复制的客户端类。

限制

资料为通用化的代码片段(名称已泛化,能力负载、恢复分支与日志行被省略),不针对具体库或框架版本;未提供完整可运行示例、依赖配置或性能数据。