Safari断网重连后WebSocket不会自动恢复,因协议无自动重连机制且Safari常不触发onclose事件,导致连接“幽灵状态”;需手动实现网络监听、心跳探测与服务端超时优化。
当Safari浏览器经历断网→重连过程后,已建立的WebSocket连接不会自动恢复,页面仍显示“已断开”,用户必须手动刷新才能重建通信,这直接导致实时消息丢失、协作中断或游戏掉线。
WebSocket协议本身不定义自动重连机制,浏览器从不主动重试;【Safari在连接断开后不会调用onclose事件,也不会重新执行new WebSocket()】。这意味着:只要你的代码里没写重连逻辑,断网再联网,socket对象就永远停留在CLOSED状态,且不会再有任何回调触发。
对比Chrome或Firefox,它们对onclose的触发更稳定;而Safari(尤其iOS 15+)在网络切换瞬间常出现event.wasClean === false但onclose根本没执行的情况——此时socket.readyState可能卡在0(CONNECTING)或1(OPEN),实际TCP连接早已消失。
打开Safari Web Inspector → Console面板,粘贴运行:
socket && socket.readyState
若返回 0 或 1,但页面已无消息收发,说明连接处于“幽灵状态”:浏览器认为它还活着,其实已被系统静默终止。这种状态在iOS锁屏唤醒、Wi-Fi切蜂窝、飞行模式开关后高频出现。
注意:不要依赖socket.bufferedAmount === 0判断是否可用,它只反映发送队列,不反映底层TCP通路。
第一步:监听网络状态变化
在页面初始化时注入以下代码:
window.addEventListener('online', () => { if (socket && socket.readyState !== WebSocket.OPEN) { reconnect(); } });
第二步:定义reconnect函数,避免重复实例化
先检查是否已有待重连任务:if (reconnectTimer) return;;再清除旧socket引用:if (socket) socket.close();;最后设置指数退避延迟启动新连接。
第三步:关键补丁——绕过Safari的onclose丢失缺陷
在创建socket后立即启动心跳探测:setInterval(() => { if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({type:'ping'})); }, 30000);。一旦send抛出异常(如InvalidStateError),立刻执行reconnect()。这比等onclose更可靠。
方法一:Nginx侧将proxy_read_timeout从默认60秒降至25秒
方法二:服务端在握手响应头中显式添加Keep-Alive: timeout=20
方法三:禁用服务端对ping帧的忽略逻辑,确保每次收到客户端ping都立即回pong——Safari对pong缺失极其敏感,连续2次未响应就会静默关闭连接,且不触发任何前端事件。