iOS 15 Safari页面失焦时WebSocket连接随机断开问题
iOS 15 Safari WebSocket 假在线问题解决办法
这个问题是iOS 15 Safari的后台资源限制导致的——浏览器会在页面失焦后静默挂起WebSocket连接,但不会触发close事件,也不更新readyState,导致客户端误以为连接还是正常的,但实际已经无法收发消息。
解决思路及实现
1. 强制心跳检测
不要依赖readyState判断连接状态,通过定时发送ping帧+服务器返回pong的方式,主动验证连接有效性:
let ws; let heartbeatTimer; let pingTimeout; // 创建WebSocket连接 function initWS() { ws = new WebSocket('ws://your-server-address'); ws.onopen = () => { startHeartbeat(); }; ws.onmessage = (e) => { // 收到服务器的pong响应,重置超时器 if (e.data === 'pong') { clearTimeout(pingTimeout); } // 处理业务消息 }; ws.onclose = () => { stopHeartbeat(); // 断开后自动重连 setTimeout(initWS, 3000); }; } // 启动心跳 function startHeartbeat() { // 每10秒发送一次ping(间隔不宜过短,避免被iOS后台节流) heartbeatTimer = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send('ping'); // 5秒内没收到pong,判定连接失效 pingTimeout = setTimeout(() => { ws.close(); }, 5000); } }, 10000); } // 停止心跳 function stopHeartbeat() { clearInterval(heartbeatTimer); clearTimeout(pingTimeout); }
2. 页面切前台主动校验
利用页面可见性API,在用户切回页面时主动触发一次连接校验:
document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { if (ws && ws.readyState === WebSocket.OPEN) { // 主动发ping,验证连接是否真的存活 ws.send('ping'); pingTimeout = setTimeout(() => { ws.close(); }, 5000); } else { initWS(); } } });
3. 服务器端配合
确保服务器收到ping消息后立即返回pong响应,这样客户端才能准确判断连接状态。
注意事项
- iOS 15 Safari后台会大幅降低定时器频率,心跳间隔建议设为10秒以上,避免定时器被节流导致心跳失效。
- 不要仅通过
ws.readyState === OPEN判断连接有效性,必须结合心跳响应结果。
内容的提问来源于stack exchange,提问作者Tao Li
相关产品推荐
相关产品推荐

