笔记

长连接上的请求,怎样等到自己的响应?

HTTP 调用通常把请求与响应包装在一起。自己实现长连接协议时,发出的 XML 进入发送队列,服务端的数据稍后从另一条接收事件流到达,调用方没有现成的响应可以 await。

这里的 Dart XMPP 客户端需要等待认证、资源绑定和心跳等结果。资源绑定是在初始化中分配当前客户端资源标识的步骤。这些请求共享连接,却必须各自认领自己的响应。

怎么把事件流包装成一次等待?

可以建立请求表,由统一接收器按请求 ID 分发;也可以为一次请求注册临时监听,匹配成功后完成 Future。项目目前采用后者,用 Completer 保存稍后完成的结果。

顺序是先注册监听,再发送请求。如果先发送、等发送方法返回后才监听,服务端响应可能已经到达,而这一层没有替新监听器保留它。

Completer 完成前检查是否已经结束,可以防止重复完成。但成功、发送失败、连接关闭和超时如何竞争,仍要由封装定义,Completer 本身不会决定谁优先。

响应里出现请求 ID,就算匹配了吗?

不够。项目当前支持元素名、额外条件和预期 ID,但 ID 检查使用字符串包含。request-1 可能匹配到 request-10,一块数据里也可能同时有两条响应。

更可靠的思路是先解析完整协议元素,再按 ID、类型与命名空间关联请求。目前这里接收的却是每块解码后的字符串,数据先进入这条事件流,之后才被追加到聊天消息缓冲。

因此等待层可能拿到半个标签或半个 ID。后面的聊天消息解析不能替这条原始监听路径补齐输入,匹配规则与输入边界需要一起调整,当前尚未完成这一步。

发送已经失败,为什么还要等到超时?

等待响应和等待发送结果是两段工作。项目调用发送方法后,没有等待它返回的 Future<bool>;即使发送层已经给出 false,外层仍可能继续等响应。

改进时可以保持先监听的顺序,同时处理发送结果。但需要定义:发送明确失败时是否立即结束,如果响应已经先到、迟到的发送错误又如何处理。

只加“完成过就不再完成”的判断能挡住重复调用,不能替业务确定优先级。验证应故意让响应先于发送返回,再试发送失败与两条响应乱序,而不是只测正常收发。

超时后,原请求还在不在?

项目超时会清理监听并抛异常。Dart 的 Future.timeout 为等待加上时间限制,源 Future 仍可能稍后完成;它也不会自动移除发送队列里的内容。

服务端可能已处理请求,所以重试需要保留请求身份,防止上一轮迟到结果被下一轮过宽的匹配条件接走。超时表达的是这次等待没有按时完成,不能代替远端状态查询。

成功、超时和捕获异常时都会取消订阅。按照 StreamSubscription.cancel,取消后不再接收事件,返回的 Future 表示底层清理何时完成。当前只是发起取消,若后续依赖资源已释放,还需要等待它。

监听尚未显式处理错误和结束事件,连接关闭后仍可能等到 timeout。

返回上一页