查看: 132|回复: 3

鸿蒙ASCF WebSocket做即时通讯的踩坑与重连优化

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙应用里做客服聊天功能时,最初采用轮询方案,每3秒调用一次 has.request 拉取新消息。亮屏状态下基本可用,但应用切到后台再回来看,消息延迟会达到5到8秒,而且频繁请求对电量消耗也不小。后来改用 ASCF 提供的 WebSocket 能力,连接建立后消息通过长连接实时推送,延迟降到毫秒级。这里把实践过程中踩过的坑和最终可用的连接管理方案整理出来。

ASCF 的 WebSocket API 从 1.0.0 开始支持,整体设计和小程序 WebSocket 很接近。不过它同时提供了两套写法:全局 API(例如 has.onSocketMessage)和 SocketTask 实例(socketTask.onMessage)。刚接触时容易搞混,尤其全局 API 在实际使用中有一些隐蔽的陷阱。

两套 API 的核心区别在于操作对象:
全局 API 操作的是最近一次 connectSocket 创建的默认连接,最多存在5个全局实例;SocketTask 操作的是 connectSocket 返回的指定连接实例,适合多连接场景。最初使用全局 API 时没太注意,等发现 connectSocket 返回 SocketTask 后就切换到了 SocketTask 写法。一个连接对应一个实例,代码结构更清晰,多个连接不会互相串消息。

连接建立时,生产环境必须使用 wss 协议,调试阶段可以用 ws。connectSocket 只是发起连接请求,不代表连接已经建立。连接真正打开是在 onOpen 回调触发时,onOpen 之前发送消息会全部丢失。因此创建连接后应先绑定事件,再等待 onOpen,之后才能调用 send 发送数据。

推荐的做法是把连接管理封装在页面实例中。例如在 Page 中维护 socketTask、heartbeatTimer 和 reconnectTimer 三个成员:connect 方法负责创建连接并绑定四类事件;onOpen 后更新连接状态并启动心跳;onMessage 对收到的字符串做 JSON.parse 并分发处理;onError 和 onClose 都停止心跳,并根据关闭码决定是否重连。

心跳是保活的关键。运营商和代理服务器会对长时间空闲的 WebSocket 连接做超时断开,所以每隔30秒发送一个 ping 数据帧,让中间设备知道连接仍然存活。弱网环境下,断线不一定触发 onError 或 onClose,TCP 连接可能处于半开状态,心跳可以在这种情况下主动发现异常并触发重连。

重连逻辑不要立刻执行。服务器可能正在重启,等5秒再重试成功率更高。如果连续失败,可以考虑指数退避策略:第一次等5秒,第二次10秒,第三次20秒。页面卸载时,需要在 onUnload 中停止心跳、清除重连定时器并主动 close 连接,避免资源泄漏。如果连接是 App 级的全局长连接,则不应放在某个 Page 里,而是放到应用生命周期的更上层统一管理。

对于只有单条连接、不关心多连接的场景,全局 API 写起来更简洁。has.connectSocket、has.onSocketOpen、has.onSocketMessage 可以直接使用。但这里有一个明显的坑:全局 API 的 onXxx 是订阅而非一次性回调。如果在 Page A 订阅了 has.onSocketMessage,跳转到 Page B 时没有取消订阅,Page B 同样会收到消息。更麻烦的是,实测发现页面卸载后回调可能仍然保留,而文档中并没有提供对应的 offSocketMessage 取消订阅方法。因此页面间跳转频繁时,使用 SocketTask 管理生命周期更可控。

在聊天/客服场景中,消息的乐观 UI 值得借鉴:用户点击发送后,先把消息渲染到自己的会话列表,再调用 socketTask.send 发送给服务器。这样用户不需要等服务器确认,体验更流畅。如果发送失败,把对应消息标记为失败提示重发。但需要注意,支付等对数据安全性要求高的场景不能这样处理,必须等服务器确认成功后再展示。

除了聊天,WebSocket 还适合股票行情、订单状态、系统通知等实时数据推送。可以在 onMessage 中按消息类型分发处理,不同类型走不同逻辑。多人协作类功能(如白板同步)也是 WebSocket 的典型场景,由服务端广播消息给所有连接。

整体看,ASCF WebSocket 与 has.request 是互补关系:普通请求用 has.request,服务端主动推送用 WebSocket。实现时需要注意几个易错点:
发送消息必须在 onOpen 之后;has.sendSocketMessage 只发往最近一次 connectSocket 创建的连接,多连接时务必改用 SocketTask;onMessage 收到的 data 是字符串,服务端返回 JSON 时记得先 JSON.parse,否则访问属性返回 undefined,排查起来很隐蔽;全局最多5个连接,超过会触发 fail,关闭连接时释放名额;wss 地址需要在 AGC 控制台配置服务器域名白名单,否则生产环境连不上,调试阶段可以用 ws 绕过,上线前必须补齐白名单。

另外,WebSocket 基于 TCP,不能走 CDN 加速。如果对延迟要求极高,可以把静态资源放到 CDN,动态数据通过 WebSocket 直连服务器。这套方案在后续优化中实测可行。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF WebSocket做即时通讯的踩坑与重连优化

干货满满,正好最近也在折腾鸿蒙的IM功能,全局API那个订阅不取消的坑确实隐蔽,感谢楼主把SocketTask和全局API的区别讲得这么清楚。心跳和指数退避的重连策略也很实用,先马住慢慢研究。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF WebSocket做即时通讯的踩坑与重连优化

感谢楼主分享,这篇实战总结太实用了。特别是全局 API 和 SocketTask 的对比,以及全局订阅回调在页面跳转后仍保留的坑,之前真没注意到,差点踩进去。心跳和指数退避重连的策略也很清晰,正好在做一个类似的客服模块,可以直接参考。另外乐观 UI 的建议也很中肯,聊天场景确实体验提升明显,安全要求高的场景另外处理这点提醒得对。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF WebSocket做即时通讯的踩坑与重连优化

感谢分享!我们项目最近也在从轮询切 WebSocket,你提到的“全局 API 是订阅不是一次性回调”这个坑太真实了,之前没注意差点在页面跳转后闹出消息串台。SocketTask 确实更可控,生命周期清晰,封装在 Page 里也方便统一管理心跳和重连。 那个“发送消息必须在 onOpen 之后”也很关键,我刚开始就是没等连接建立就发消息,结果丢了一堆,调试半天才发现是时序问题。另外心跳间隔 30 秒和指数退避重连的思路很实用,弱网下 TCP 半开状态确实靠心跳才能感知到。 乐观 UI 那部分也认同,聊天场景这么干体验提升明显。就是全局 API 没有 offSocketMessage 这点挺麻烦的,还是乖乖用 SocketTask 省心。收藏了,回头按你的方案改造一下试试!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-8-12 16:28 , Processed in 0.022994 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部