uni-app跨端WebSocket需统一用uni.connectSocket,但各端协议(wss://强制)、域名白名单、参数编码、onOpen后发消息、数据类型判断、粘包处理及单例管理等必须差异化适配。

真机跑不通、小程序连不上、H5能用App炸开——这不是代码写错了,是跨端 WebSocket 的协议和生命周期没对齐。uni-app 的 uni.connectSocket 看似统一,但 iOS/Android/微信/支付宝/H5 底层实现差异极大,硬套 H5 写法必踩坑。
为什么 uni.connectSocket 在 App 和小程序里连不上
多数人卡在这一步:本地 ws://localhost:8080 调试时 H5 正常,一真机就 onOpen 不触发。根本原因不是“网络问题”,而是平台拦截:
- iOS/Android 原生 WebView 默认屏蔽非加密的
ws://,只放行wss://;微信/支付宝小程序强制要求合法域名且必须备案,ws://直接被拒绝 -
manifest.json里没填 WebSocket 合法域名 → App 端连接被静默丢弃(不报错,也不触发onError) - URL 带未编码的查询参数(如
?token=abc+def)→ App 端解析失败,连接中断
解决办法只有三条路:wss:// 上线域名 + manifest 配置白名单;开发阶段启用「WebView 调试」;或打包自定义基座(仅限 App 端临时调试)。
onMessage 收到 ArrayBuffer 却 JSON.parse 报错
App 端和小程序的 onSocketMessage 回调中,res.data 类型不可控:服务端发文本,它可能是 String;发二进制,它就是 ArrayBuffer。直接 JSON.parse(res.data) 必崩。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 务必先判断类型:
typeof res.data === 'string'才解析,否则用new TextDecoder().decode(res.data)转字符串 - 后端发多条消息时容易粘包(尤其高并发场景),App 端可能把 3 条合并成 1 个
ArrayBuffer,或拆成 2 次回调。别依赖单次回调 = 单条消息 - 发送前统一
JSON.stringify(),禁用sendSocketMessage({ data: {a:1} })这种传对象写法 —— App 端会静默失败
App 切后台后连接自动断开,重连策略怎么设才合理
这不是 bug,是 iOS/Android 系统行为:App 进入后台 10–30 秒内,系统强制回收 WebSocket 连接。强行轮询重连只会耗电、被杀进程、触发平台限频。
-
uni.onHide触发时,立刻uni.closeSocket()并清空状态,避免残留连接干扰后续逻辑 -
uni.onShow里检查socketTask是否还存活(readyState === 1),断了再连,别无脑重连 - 重连间隔必须指数退避:首次失败等 3 秒,第二次 6 秒,第三次 12 秒,最多试 3 次 —— 防止网络抖动时雪崩式请求
- 真正强实时场景(如客服消息),WebSocket 只管前台活跃期,离线消息必须靠个推/华为 Push 补位
多个页面都要用 WebSocket,怎么避免重复连接
每个页面都调 uni.connectSocket,结果就是连接数爆炸、消息乱序、Token 冲突。uni-app 没内置连接池,得自己控单例。
- 把连接封装成全局函数,返回的
socketTask存到uni.$socket或globalData.socket,所有页面共用一个实例 - 页面间通信走事件总线(
uni.$emit/uni.$on),而不是各自调socketTask.send - 消息发送前加简单帧头(如 4 字节长度字段),收到后手动切分 —— 这是解决 App 端粘包最轻量可靠的方案,比等服务端改协议现实得多
跨端 WebSocket 最难的不是连上,而是连得稳、断得清、消息不丢。协议、生命周期、数据格式这三关没对齐,再多的封装类也救不了。

















