You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决被动切换网络时Socket.io客户端无法自动重连的问题

问题根因
  • 仅配置.reconnects(true)不足以覆盖网络切换场景的重试逻辑,当前客户端Socket.io配置缺失重连相关的核心参数
  • iOS系统被动切换网络时,旧的Socket连接处于半开状态,客户端默认要等到完整的ping/pong超时周期才会触发重连,若重连尝试次数、间隔参数配置不合理会直接中断重试
  • 未监听系统网络状态变化事件,无法在网络恢复的第一时间主动触发重连逻辑
  • 服务端原有逻辑漏写了databaseID的赋值,导致旧连接清理逻辑完全不生效
客户端修复方案

1. 补全SocketManager配置和事件监听

var socket: SocketIOClient?
fileprivate var manager: SocketManager?

func establishConnection(_ completion: (() -> Void)? = nil) {
    // 先销毁旧实例避免重复连接冲突
    if let oldSocket = socket {
        oldSocket.disconnect()
    }
    manager?.disconnect()
    
    let socketUrlString: String = serverURL
    // 补全重连相关配置
    self.manager = SocketManager(socketURL: URL(string: socketUrlString)!, config: [
        .forceWebsockets(true),
        .log(false),
        .reconnects(true),
        .reconnectAttempts(-1), // 配置无限次重试,可根据业务需求调整上限
        .reconnectWait(2), // 初始重试间隔2秒
        .reconnectWaitMax(10), // 最大重试间隔10秒,避免重试过于频繁
        .forceNew(true), // 每次连接创建新会话,避免旧连接缓存影响
        .extraHeaders(["id": myDatabaseID])
    ])
    self.socket = manager?.defaultSocket
    
    // 监听断开连接事件,非主动断开时触发重连
    self.socket?.on(clientEvent: .disconnect) { [weak self] data, ack in
        guard let self = self else { return }
        if self.socket?.status != .disconnected {
            self.socket?.connect()
        }
    }
    
    // 监听连接成功事件,重连后重新加入业务房间
    self.socket?.on(clientEvent: .connect) { [weak self] data, ack in
        guard let self = self else { return }
        // 此处补充加入用户所在房间的逻辑,例如向服务端发送joinRoom事件携带房间ID
        completion?()
    }
    
    self.socket?.connect()
}

2. 增加系统网络状态监听,网络恢复时主动触发重连

import Network

private let networkMonitor = NWPathMonitor()

// 在App启动或Socket初始化阶段调用
func setupNetworkMonitor() {
    networkMonitor.pathUpdateHandler = { [weak self] path in
        guard let self = self else { return }
        // 网络恢复且当前Socket未连接时,主动触发连接
        if path.status == .satisfied && self.socket?.status != .connected {
            self.establishConnection()
        }
    }
    let monitorQueue = DispatchQueue(label: "SocketNetworkMonitor")
    networkMonitor.start(queue: monitorQueue)
}
服务端修复方案
const io = require('socket.io')(http, {
  pingTimeout: 15000, // 调整为15秒,避免过短超时导致误判
  pingInterval: 5000 // 每5秒发送一次ping包,更快感知连接断开
});

io.on('connection', onConnection);

function onConnection(socket){
  let headerDatabaseID = socket.handshake.headers.id
  // 补全databaseID赋值,原逻辑漏写该行导致旧连接判断失效
  socket.databaseID = headerDatabaseID
  
  // 清理同账号的旧连接
  for (let [id, connectedSocket] of io.sockets.sockets) {
    if (connectedSocket.databaseID == headerDatabaseID && socket.id != id) {
      connectedSocket.disconnect()
      break
    }
  }
  
  // 监听客户端重连后的加入房间请求
  socket.on('joinRoom', (roomId) => {
    socket.roomId = roomId
    socket.join(roomId)
  })
}
注意事项
  • 重连成功后必须主动向服务端发送加入房间请求,Socket.io服务端不会自动保留断开连接客户端的房间成员身份
  • 不要在每次连接时重复创建SocketManager实例,复用单实例即可,避免内存泄漏和重复连接冲突
  • 可根据业务需要调整pingInterval和pingTimeout的数值,平衡连接断开感知速度和服务器性能消耗

内容的提问来源于stack exchange,提问作者nickcoding2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 11:15:04