ios Wifi上的多点连接数据流问题

lg40wkob  于 2023-02-26  发布在  iOS
关注(0)|答案(2)|浏览(216)

我正在使用多点连接在通过Wifi连接的几个设备之间同步3D SceneKit环境中节点的移动。
节点移动在主设备上计算并发送到从设备,然后从设备将节点设置到接收到的位置。移动通过主设备的SceneView的渲染器循环以及每个从设备打开并使用MultipeerConnectivity框架管理的数据流发送。
下面的视频显示了结果,以及从机(右)上的球抖动问题,这是由于通过流接收数据时每0.6-0.7秒有规律地暂停一次。左上角的计数器显示没有丢失数据包。接收到的数据的完整性也没有问题。

模拟器中不存在这种非常有规律的从机暂停,但只有在真实的设备上运行时才存在,无论是什么设备(iPhone或iPad,旧的或新的)。

  • 是否有办法找出导致从设备定期暂停的原因?*
  • 让从线程上的输入流在专用线程/runloop上执行,而不是在主线程的runloop上执行,这有意义吗?*
    低于执行水平

主多点连接初始化

PeerID = MCPeerID(displayName: "Master (" + UIDevice.current.name + ")")
    MPCSession = MCSession(peer: PeerID, securityIdentity: nil, encryptionPreference: .none)
    MPCSession.delegate = self
    
    ServiceAdvertiser = MCNearbyServiceAdvertiser(peer: PeerID, discoveryInfo: nil, serviceType: "ARMvt")
    ServiceAdvertiser.delegate = self
    ServiceAdvertiser.startAdvertisingPeer()

从属多对等连接初始化

PeerID = MCPeerID(displayName: "Slave (" + UIDevice.current.name + ")")
    MPCSession = MCSession(peer: PeerID, securityIdentity: nil, encryptionPreference: .none)
    MPCSession.delegate = self
    
    ServiceBrowser = MCNearbyServiceBrowser(peer: PeerID,  serviceType: "ARMvt")
    ServiceBrowser.delegate = self
    ServiceBrowser.startBrowsingForPeers()

发送数据的函数,在呈现器函数中调用

func MPCSendData(VCRef: GameViewController, DataToSend: Dictionary<String, Any>, ViaStream: Bool = false)
{
    var DataFilledToSend = DataToSend
    var DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: true)
    var TailleData: Int = 0
    var NewTailleData: Int = 0
    
            
    if ViaStream    // Through the stream
    {
            // Filling data to have a constant size packet
            // kSizeDataPack is set to 2048. The bigger it is the worst is the jittering.
        VCRef.Compteur = VCRef.Compteur + 1
        VCRef.Message.SetText(Text: String(VCRef.Compteur))
        DataFilledToSend[eTypeData.Compteur.rawValue] = VCRef.Compteur
        DataFilledToSend[eTypeData.FillingData.rawValue] = "A"
        TailleData = DataConverted.count
        DataFilledToSend[eTypeData.FillingData.rawValue] = String(repeating: "A", count: kSizeDataPack - TailleData)
        DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: false)
        NewTailleData = DataConverted.count
        DataFilledToSend[eTypeData.FillingData.rawValue] = String(repeating: "A", count: kSizeDataPack - TailleData - (NewTailleData - kSizeDataPack))

        DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: false)
        
        if VCRef.OutStream!.hasSpaceAvailable
        {
            let bytesWritten = DataConverted.withUnsafeBytes { VCRef.OutStream!.write($0, maxLength: DataConverted.count) }

            if bytesWritten == -1 { print("Erreur send stream") }
        } else { print("No space in stream") }
    }
    else    // Not through the stream
    {
        let Peer = VCRef.MPCSession.connectedPeers.first!
        try! VCRef.MPCSession.send(DataConverted, toPeers: [Peer], with: .reliable)
    }
}

通过从机上的流接收数据时调用的函数

func stream(_ aStream: Stream, handle eventCode: Stream.Event)
{
    DispatchQueue.main.async
    {
        switch(eventCode)
        {
        case Stream.Event.hasBytesAvailable:
            let InputStream = aStream as! InputStream
            var Buffer = [UInt8](repeating: 0, count: kSizeDataPack)
            let NumberBytes = InputStream.read(&Buffer, maxLength: kSizeDataPack)
            let DataString = NSData(bytes: &Buffer, length: NumberBytes)
            if let _ = NSKeyedUnarchiver.unarchiveObject(with: DataString as Data) as? [String:Any] //deserializing the NSData
            {
                ProcessMPCDataReceived(VCRef: self, RawData: DataString as Data)
            }
            
        case Stream.Event.hasSpaceAvailable:
            break
            
        case Stream.Event.errorOccurred:
            print("ErrorOccurred: \(String(describing: aStream.streamError?.localizedDescription))")
            
        default:
            break
        }
    }
}

处理接收到的数据的函数

func ProcessMPCDataReceived(VCRef: GameViewController, RawData: Data)
{
    let DataReceived: Dictionary = (try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(RawData) as! [String : Any])

    switch DataReceived[eTypeData.EventType.rawValue] as! String
    {
    case eTypeEvent.SetMovement.rawValue:
        VCRef.CurrentMovement = eTypeMovement(rawValue: DataReceived[eTypeData.Movement.rawValue] as! String)!
     
    case eTypeEvent.SetPosition.rawValue:
        VCRef.Ball.position = DataReceived[eTypeData.Position.rawValue] as! SCNVector3
            
    default:
        break
    }
}
3lxsmp7m

3lxsmp7m1#

看起来您正在主线程上分派工作。虽然我不认为解包数据真的会导致这些定期暂停,但您可能遇到了数据处理瓶颈。换句话说,接收、解包并重新定位节点(这也会导致隐式SceneKit事务)可能刚好足以使设备变慢。看起来你的代码足够紧凑,可以让你把所有的东西都分派到一个队列中。我建议你试试看是否有新的行为。试试DispatchQueue.global(),或者更好的。用DispatchQueue(label:"StreamReceiver", qos: .userInteractive)制作自己的。我认为异步调度在这里非常好。
编辑:实际上,看得更多,我认为这可能与SceneKit事务有关。看起来你不是真的在“暂停”,而是在减速。我提到了隐式事务--当你定位节点时,显式地开始,设置动画时间,并结束一个SCNTransaction。我一直在使用的一个代码片段是:

func sceneTransaction(_ duration: Int? = nil,
                      _ operation: () -> Void) {
    SCNTransaction.begin()
    SCNTransaction.animationDuration =
        duration.map{ CFTimeInterval($0) }
            ?? SCNTransaction.animationDuration
    operation()
    SCNTransaction.commit()
}

尝试使用重新定位代码调用它,或者只是将事务start/animationTime/end粘贴在您的块周围。祝您好运!
编辑2:好的,还有一件事。如果这对你的用例有意义,确保你已经***停止浏览和广告。这是一个昂贵的网络,而且可能会拖累整个子系统。

hkmswyz6

hkmswyz62#

我也遇到过这个问题,这似乎取决于设备所处的环境。
以下是我尝试的结果:在通过多点连接连接的两个设备上,发送方每秒发送数据60次,接收方打印日志。2有时每秒会有0.1-0.2秒的轻微停顿,但有时工作正常。
最后,我用CoreBuetooth框架替换了多点连接框架,它似乎没有这个问题。

相关问题