登录一直在连接中,到底在等什么?
聊天 App 登录时,Socket 连上只是开始。还要初始化 XMPP 协议流、认证账号、绑定当前客户端的资源标识,才能正常收发业务消息。
如果这些步骤都叫“连接中”,用户遇到长时间等待,日志也很难指出卡在哪。这个 Dart 客户端把连接阶段分开记录,但超时、重试和资源清理仍有几处容易误解的地方。
网络连上了,为什么还说未连接?
可以只对外提供一个可用状态,也可以保留详细阶段,再从中计算是否可收发。项目保留断开、连接中、网络已连接、认证中、认证完成和准备就绪等状态。
对外的 isConnected 实际判断是否 ready,表示协议初始化完成。因此它为 false 时,Socket 未必断了,可能仍在认证或绑定。
底层发送也不能全部等 ready,因为认证和绑定本身就要发协议数据。当前状态更新主要是赋值与广播,合法转换仍散落在调用流程中;定义了状态枚举,还没有自动形成统一的转换约束。
设了 20 秒超时,为什么登录更久?
超时可以限制一次阶段、一次连接尝试,或者整个登录过程。三种预算需要明确,不能看到一个数字就把它当成总上限。
项目的 Token 登录按阶段等待,当前配置如下:
| 范围 | 等待值 |
|---|---|
| 网络建立 | 10 秒 |
| 认证 | 8 秒 |
| 协议流等待 | 5 秒 |
| 绑定 | 5 秒 |
| 单轮外围等待 | 默认 20 秒 |
阶段与外围计时同时存在,不能简单相加,外围可能先超时。外围计时又在重试循环内部,每轮重新开始;最多两次重试,加首次共三轮,因此整个登录没有被限制在 20 秒内。
重试间隔从 800 毫秒起按倍数增加并封顶 5 秒,这条路径两次分别等 800 毫秒和 1.6 秒。若要限制总耗时,可以在入口记录截止时间,各阶段共用剩余预算,当前实现尚未如此处理。
外围超时,为什么旧连接还可能回来?
项目用连接操作与超时进行 Future.any 竞争,最先结束的决定结果,但其他任务不会自动取消。第一轮外围已经超时,内部认证或绑定仍可能稍后完成。
如果旧尝试继续修改 Socket、连接状态或 Token,就可能干扰下一轮。可选做法是为每次尝试保留身份,结果回来时只允许当前尝试生效,并实际清理可取消资源。项目的消息补偿层已有账号和代次检查,底层连接尝试仍需补类似约束。
心跳也不是精确的总计时。当前单次等 15 秒,连续三次失败进入连接丢失处理,另有定时调度与重试路径,不能直接理解成 45 秒必然断线。多个恢复入口尚未统一安排,也需要避免各跑一套重试。
清理函数调用了,为什么资源可能没释放?
外层清理入口先把“正在清理”标记设为 true,再调用内层函数;内层开头又发现该标记为 true,于是直接返回。防重复的逻辑反而挡住了真正的释放过程。
这条问题出现在外层调用内层的路径,直接调用内层的入口需要另外检查。后续可以由外层管理重复调用,内层只负责释放;验收看定时器、订阅、Socket 和排队任务是否结束,而不只看状态是否变成断开。
错误分类也尚有不足:认证错误会阻止重试,但最终包装可能让其他错误也表现成认证异常,部分判断还依赖异常文本。需要保留阶段和明确错误类型,才能让界面区分重新登录与稍后重连。