本节导读:请求是问一句答一句,长连接是通了就一直在线。客服消息、秒杀行情、协作提醒都靠它。本节用连接的状态机把 uni.connectSocket 的生命周期管起来:心跳保活、指数退避重连、切后台的端差异处理,把"消息时有时无"变成可推导的工程问题。
商城客服功能上线首周的反馈清单:切到后台回来消息收不到、锁屏半小时后界面显示在线但发不出消息、弱网下偶发"假在线"。三个症状同一病根——代码把"建立连接"当成一次性动作,而长连接的真实生命周期是建立、活着、断开、重连的循环。修法不是打补丁,而是先画状态机再写代码:连接在任意时刻必须处于且仅处于一个明确状态,每次状态迁移都有触发条件与善后动作。
状态机的价值立即可见:"假在线"就是状态机缺失时的产物——界面以为还在线,实际通道已死。有了显式状态,UI 只信状态字段,不信"没收到断开消息"这种沉默。
长连接的两大保活件。心跳回答"我怎么知道通道还活着":客户端定时发一个 ping 帧,服务端回 pong;若连续若干个周期没等到回音,判定通道已死,主动断开并进入重连。间隔取三十秒上下——太密耗电也扰网关,太疏则死链发现慢。重连回答"死了怎么爬起来":立刻猛看重连在服务端重启或弱网抖动时会形成连接风暴,标准答案是指数退避加随机抖动,首次一秒、翻倍到上限六十秒封顶,每次加随机偏移把集中重试打散。落地成一个连接管理器:
// utils/socket.js —— 长连接管理器骨架 const state = { status: 'closed', socket: null, retry: 0, pingTimer: null }; function connect() { state.status = 'connecting'; state.socket = uni.connectSocket({ url: 'wss://msg.shop.com/ws?token=' + uni.getStorageSync('shop_token'), success: () => {}, complete: () => {} }); state.socket.onOpen(() => { state.status = 'online'; state.retry = 0; startHeartbeat(); }); state.socket.onMessage((res) => { const msg = JSON.parse(res.data); if (msg.type === 'pong') return; uni.$emit('ws-message', msg); // 分发给订阅的页面 }); state.socket.onClose(() => scheduleReconnect('closed')); state.socket.onError(() => scheduleReconnect('error')); } function startHeartbeat() { clearInterval(state.pingTimer); state.pingTimer = setInterval(() => { send({ type: 'ping' }); }, 30000); } function scheduleReconnect(reason) { clearInterval(state.pingTimer); state.status = 'reconnect-wait'; if (state.retry >= 5) { state.status = 'closed'; return; } const delay = Math.min(60000, 1000 * Math.pow(2, state.retry)) + Math.random() * 500; state.retry += 1; setTimeout(connect, delay); } function send(payload) { if (state.status !== 'online') return false; state.socket.send({ data: JSON.stringify(payload) }); return true; }
三处细节是这份骨架的筋骨:send 前检查状态,拒绝在非在线态发消息,"发出去了却没到"的幻觉消失;重试计数在 onOpen 清零,成功后从一秒重新起步;消息分发走 uni.$emit 事件总线,页面订阅即得,连接管理器不认识任何页面。
长连接在应用切后台后的命运,各端规矩不同,这是端差异最密集的一处。微信小程序端:切后台约五秒后宿主可能挂起连接,返回前台的 onShow 里应主动检查状态、已断则立即重连;App 端:后台存活时间更长,但系统省电策略会掐长连接,回前台同样要"先探活再说话";H5 端:页签切走浏览器不一定断连,但移动端浏览器回收更狠,visibilitychange 事件是探活时机。统一策略收敛成一句话:回前台探活、失败重连、重连后拉一次增量——断线窗口期里漏掉的消息,靠重连成功后向服务端拉取未读增量补齐,长连接负责实时、HTTP 兜底完整,两条腿缺一不可。
⚠️ 小程序端的 wss 连接地址必须在小程序后台配置合法域名,且不支持 ws 明文与自定义端口——联调期"连不上"先查域名白名单,再查代码。
数据流四站走完,下一章出海关去各端领地:原生 API、平台特有功能与小程序的限制清单。