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

SwiftUI多端连接问题:重置服务后MCNearbyServiceBrowser无法连接

解决Multipeer Connectivity重置PeerID后连接失败/首次Join无效问题

问题背景

我开发的SwiftUI多端连接应用支持单设备作为主机,其他设备加入会话,还实现了大厅功能——设备加入前可查看成员、头像信息以保证头像唯一。

为更新MCNearbyServiceAdvertiser的discoveryInfo,我参考方案在调用reset()时重置了MCPeerID,虽然discoveryInfo能正常更新,但出现了新的连接问题:

[MCNearbyServiceAdvertiser] PeerConnection connectedHandler (advertiser side) - error [Unable to connect].
[MCNearbyServiceAdvertiser] PeerConnection connectedHandler remoteServiceName is nil.
[connection] nw_socket_handle_socket_event [C3:1] Socket SO_ERROR [54: Connection reset by peer]
...
[AGPSession] Couldn't check in handle iDstIDs (-1).
[AGPSession] Couldn't check in handle iDstIDs (0).

2023年7月12日调整逻辑后:仅在设备断开会话时重置服务,加入设备仅在尝试加入时才初始化MCNearbyServiceAdvertiser并复用主机的discoveryInfo,但问题变为首次点击Join按钮无法连接,必须点击第二次才行。

核心问题分析

Multipeer Connectivity的MCPeerID是设备在会话中的唯一标识,重置PeerID后旧的广告/浏览会话状态未完全清理,导致连接握手时身份不匹配;调整后的逻辑中,首次Join时MCNearbyServiceAdvertiser初始化时机或状态同步存在延迟。

具体排查与修复步骤

  • 严格控制PeerID与服务实例生命周期
    不要在会话未完全断开时重置MCPeerID,必须确保所有MCNearbyServiceAdvertiser、MCNearbyServiceBrowser、MCSession实例都已销毁并置为nil后,再生成新的MCPeerID;重置服务时先调用advertiser.stopAdvertisingPeer()和browser.stopBrowsingForPeers(),销毁实例后再创建新的PeerID和服务实例。

  • 修复首次Join的状态同步问题
    加入设备初始化MCNearbyServiceAdvertiser后,不要立即发起连接,等待advertiser完成初始化(可通过监听NSNotificationCenter的MCNearbyServiceAdvertiserDidStartAdvertisingPeerNotification通知,确认广告服务启动后再调用invitePeer:toSession:withContext:timeout:);确保调用invitePeer时,主机的MCSession处于活跃且可接受连接的状态,主机端不要在收到邀请前重置服务。

  • 清理旧的会话缓存
    在重置服务时,调用session.disconnect()后,等待MCSessionDelegate的session:peer:didChangeState:回调确认所有连接断开,再进行后续操作;避免重复创建MCSession实例,同一时间只保留一个活跃的会话实例。

  • 检查discoveryInfo的传递逻辑
    加入设备复用主机的discoveryInfo时,确保内容最新且完整,不要传递nil或不完整的字典;主机更新discoveryInfo后,必须停止广告服务再重新启动,才能让浏览设备获取到最新信息。

关键代码示例

// 主机端重置服务的正确流程
func resetSession() {
    // 停止广告和浏览
    advertiser?.stopAdvertisingPeer()
    browser?.stopBrowsingForPeers()
    // 断开会话
    session?.disconnect()
    // 销毁旧实例
    advertiser = nil
    browser = nil
    session = nil
    // 生成新的PeerID
    let newPeerID = MCPeerID(displayName: UIDevice.current.name)
    // 重新初始化会话、广告、浏览实例
    session = MCSession(peer: newPeerID, securityIdentity: nil, encryptionPreference: .required)
    session?.delegate = self
    advertiser = MCNearbyServiceAdvertiser(peer: newPeerID, discoveryInfo: updatedDiscoveryInfo, serviceType: "your-service-type")
    advertiser?.delegate = self
    advertiser?.startAdvertisingPeer()
}

// 加入端等待广告启动后再发起连接
func joinSession(with hostPeer: MCPeerID) {
    let peerID = MCPeerID(displayName: UIDevice.current.name)
    session = MCSession(peer: peerID, securityIdentity: nil, encryptionPreference: .required)
    session?.delegate = self
    advertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: hostDiscoveryInfo, serviceType: "your-service-type")
    advertiser?.delegate = self
    
    // 监听广告启动通知
    NotificationCenter.default.addObserver(forName: .MCNearbyServiceAdvertiserDidStartAdvertisingPeer, object: advertiser, queue: .main) { [weak self] _ in
        guard let self = self, let session = self.session else { return }
        // 发起邀请
        self.browser?.invitePeer(hostPeer, to: session, withContext: nil, timeout: 30)
        // 移除监听
        NotificationCenter.default.removeObserver(self, name: .MCNearbyServiceAdvertiserDidStartAdvertisingPeer, object: self.advertiser)
    }
    
    advertiser?.startAdvertisingPeer()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:05:33