feat(network): 新增原生 WebSocket(SocketTask)能力 - #296
Conversation
Android、iOS、HarmonyOS 三端接入 connectSocket / sendSocketMessage / closeSocket 与 open、message、error、close 的 on/off,任务态 SocketTask 与 全局遗留 API 两种形态共用同一套校验顺序和错误文案。架构见 docs/websocket-architecture.md。 顺带修掉两个不止影响 WebSocket 的问题:iOS 和 HarmonyOS 的共享失败回调路径 只把错误放进 data.errMsg,而 JS 的 fail 读的是 res.errMsg,导致这两端所有走 该路径的接口都拿不到错误信息;iOS 的 DMPService.fromContainer 给每条消息各起 一个不受管的 Task,容器回传到 JS 的顺序取决于线程调度,实测 closeSocket 上 稳定出现 complete 早于 success,改为排进引擎 JS 线程的串行队列。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Regenerated via scripts/sync-compatibility-reference.js so the WebSocket entry matches docs/API-Reference.md.
不带参数的 remove() 原来会清空表里所有常驻回调,wx.offLocationChange() 正是这么调的,会连带摘掉其它接口的监听;改为只按 id 摘,整表清空另给 clear()。 一次性回调改成先摘再调,回调抛异常不再留下记录。success / fail 成对登记, 任一条触发时两条一起摘掉,成功路径不再永久留下没走的那条。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cb47fa7 to
11ecd9e
Compare
invoke 原来投递线程安全函数之后才取 future,ArkTS 线程可能已经跑完回调并销毁 promise,共享状态被释放,future.get() 收尾时解引用已释放内存。模拟器复现并用 llvm-symbolizer 定位到 js_thread.cpp:206,改为投递前取 future。 同时修掉同一层的其他资源问题:三处 JS_DupValue 未配对释放、六条失败路径的 packet 泄漏、console 每个参数的 strdup 缓冲区泄漏、调试模式下两个临时 JSValue 泄漏;三个桥函数补齐异常安全,返回异常哨兵时挂上真正的异常对象,日志路径改为 不污染 runtime 的待处理异常。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # fe/package.json
oxlint 1.77 收紧了这条规则,仓库锁文件停在 1.76,持续集成用 --no-frozen-lockfile 装到 1.77 才报出来。这里要把扩展抛出的原值 原样抛回,包成 Error 会丢信息,所以加豁免注释而不是改行为。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@dos1in 好了 |
|
44f0561 to
8dec2d5
Compare
- SocketTask 不再提供 readyState,去掉为维护它而挂的三个内部监听。 - 三端不再注入 Origin,改为注入容器固定的 Referer https://servicedimina.com/{appId}/{versionCode}/page-frame.html,与 request 一致。 - 全局遗留监听按事件存一组回调,off 只摘指定的那个,不传参数才是全清。 - 非有限数的 code 和 timeout 不再被 JSON 序列化成 null 让原生退回默认值; iOS 上原来会直接抛 NSInvalidArgumentException 崩掉宿主。 - 连接超时传给平台传输层:Android 设 OkHttp connectTimeout,iOS 设 URLRequest.timeoutInterval,各留 1 秒余量让容器自己的定时器先到。 - 同名不同大小写的 header 字段在下发前折叠成一条,避免三端各行其是。 - 示例宿主打开小程序时带上 versionCode,否则注入的 Referer 版本段一直是 0。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4780737 to
3c3afdd
Compare
|
@dos1in 好了,我又修复了接口对齐的问题 |
|
|
我 review 了一遍,发现接口实现上, AI 把很多 web 的 socket(根因是 dimina 之前项目里的错误定义)当成了要实现的 API,等我把这些都删删,再对着微信文档对齐对齐。 |
a4c5480 to
f6e5e5e
Compare
f6e5e5e to
603565e
Compare
…96-review # Conflicts: # shared/jssdk/config.json # shared/jssdk/main.zip
|
@dos1in 好了 |
|
fe/packages/service/src/api/core/network/websocket/index.js 第 238 行: 根因关联位置: 同文件第 190~196 行:clearAllTimers() 会取消单例中的全部定时器。 [P1] iOS 需要按 Engine 隔离定时器,避免其他小程序冻结当前事件队列 这里依赖 setTimeout 异步执行 drainEventDispatchQueue,但 iOS 的 DMPEngineTimer 是进程级单例:所有 Engine 的定时器都保存在同一个 timers 字典中,而任一 DMPEngine.destroy() 都会调用 DMPEngineTimer.shared.clearAllTimers(),取消全部 Engine 的定时器。 在两个小程序同时运行时,如果小程序 B 已经把 WebSocket error/close 放入队列并设置了 eventDispatchScheduled = true,此时小程序 A 销毁或重启,就可能取消 B 的这个派发定时器。由于 eventDispatchScheduled 只有在 drainEventDispatchQueue() 执行时才会重置为 false,B 的队列会永久卡住;后续事件只会继续入队,不会再创建新的 drain 定时器,导致 error、close 以及之后的 WebSocket 事件全部无法送达。 建议给定时器记录所属 Engine/JSContext,销毁时只清理当前 Engine 的定时器,并增加双 Engine 回归测试:A 销毁后,B 的 setTimeout、setInterval 和 WebSocket error/close 派发仍应正常执行。 |
实现结果
公开 API
Android、iOS、HarmonyOS、Web 已接入以下能力:
wx.connectSocketwx.sendSocketMessage、wx.closeSocketwx.onSocketOpen、wx.onSocketMessage、wx.onSocketError、wx.onSocketCloseSocketTask.send、SocketTask.closeSocketTask.onOpen、SocketTask.onMessage、SocketTask.onError、SocketTask.onCloseSocketTask只公开微信文档和wechat-miniprogram/api-typings5.2.3 声明的 6 个方法;不公开readyState、状态常量、socketId、构造器、SocketTask.off*或wx.offSocket*。参数与连接规则
wx.connectSocket只接受wss://地址;TLS 与证书由系统网络栈校验。connectSocket不支持 Promise;全局sendSocketMessage/closeSocket支持 Promise;SocketTask的 6 个方法返回void。errMsg,不增加errno或内部字段。SocketTask.send/wx.sendSocketMessage支持字符串和ArrayBuffer。SocketTask.close/wx.closeSocket支持code、reason、success、fail、complete;reason最长 123 个 UTF-8 字节。wx.closeSocket只关闭已经打开的全局目标;目标仍在握手、正在关闭或已经终态时失败,不扫描或关闭其他SocketTask。事件与平台实现
onMessage返回{ data },二进制消息为ArrayBuffer。onError返回{ errMsg },onClose返回{ code, reason }。onSocketOpen返回{ header }。onOpen返回{ header, profile },profile使用平台可取得的网络阶段指标。onOpen只返回{ header },不使用回填时间冒充profile;浏览器无法读取握手响应头,因此 Web 的header为空对象。https://servicedimina.com/{appId}/{versionCode}/page-frame.html形式的Referer;使用servicedimina.com是 Dimina 产品策略。浏览器禁止脚本设置 WebSocket 握手头,Web 的Referer、Origin由浏览器控制。appId隔离,在 Web 按MiniApp实例隔离。同批修复
wx未登记属性不再动态生成代理函数,避免私有 API 被误暴露。Origin、子协议和自定义请求头限制。完整 API 和平台差异见
docs/websocket-architecture.md。潜在问题
wss,但尚未执行微信生产环境的服务器合法域名白名单;系统证书校验不能替代后台域名配置策略。forceCellularNetwork四端未生效;tcpNoDelay仅 Android 生效;perMessageDeflate四端未按参数值控制,Android OkHttp 和浏览器可能自行协商压缩。open.profile,会自动添加Origin,只提供第一条重复响应头,且不能稳定区分连接拒绝、DNS 失败和平台超时。Origin;Web 端传入的header不会上线传输,open.header为空对象,open.profile省略。DMPEngineTimer.registerTimerFunctions新增必填executor参数,仓库外仍调用旧签名的代码会编译失败。