【问题标题】:Multipeer Connectivity Framework - Lost Peer stays in SessionMultipeer Connectivity Framework - Lost Peer 留在会话中
【发布时间】:2014-05-25 17:21:15
【问题描述】:

考虑到社区遇到的所有错误,我想知道这个 Multipeer Connectivity 框架是否已准备好在现实世界中使用。我认为我的设置是正确的,但是我尝试过的所有其他示例项目都遇到了类似的问题。

我遇到的问题可能与Bonjour固有的某些问题有关,我无法弄清楚,但基本上问题如下:

  • 我有一个活跃的MCSession 和许多同行。
  • 现在,如果设备处于会话中,然后强制退出,则“对等方”会无限期地保持连接。
  • 我无法强制该用户退出,即使该对等方调用了 browser:lostPeer: 方法,并且甚至不再在浏览器中显示为 “附近”。
  • 不会为该对等方调用 session:peer:didChangeState: 方法。
  • 当强制退出的对等方返回应用程序时,它们会再次被browser:foundPeer:withDiscoveryInfo:“找到”,但仍存在于session.connectedPeers NSArray 中。显然,他们仍然没有收到有关会话的任何数据或更新,也没有实际连接。
  • 似乎唯一可以将该原始对等点注册为MCSessionStateNotConnected 到会话的方法是将该对等点重新连接到原始会话。然后重复调用 session:peer:didChangeState:,其中 peerID 的新实例是 MCSessionStateConnected,并且在 peerID 的旧实例调用 MCSessionStateNotConnected 之后不久。

示例聊天应用程序很好地证明了这个问题:https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html

由于似乎没有任何方法可以从会话中手动强制删除对等点,我应该怎么做?我应该尝试以某种方式重建会话吗?

这个框架看起来有点乱,但我试图保留判断力!

【问题讨论】:

  • 我有一个可以工作的应用程序,但需要将它扩展到超过 8 个对等点,现在它坏了:(。到目前为止我发现了一个问题,在进入后台时不要无意中对 MC 对象有强引用(我知道,它的应用范围不仅限于 MC.. 但提醒会有所帮助!)
  • 我也有同样的问题。有时 MCSession 的 session:peer:didChangeState: 不会被 MCSessionStateNotConnected 调用以用于断开连接的对等点。当多个对等点连接时,一些对等点会收到通知,其他对等点不会。有时每个人都会得到正确的通知。我已经能够找到它的根本原因。即使对等方正在调用其断开方法,也会发生这种情况。

标签: ios session multipeer-connectivity


【解决方案1】:

我对此类问题的唯一解决方法是在会话和对等方之间建立 1-1 关系。它使广播的发送变得复杂,但至少允许通过断开/删除会话本身进行对等级断开连接和清理。

更新

为了详细说明我的原始答案,为了能够向连接的对等方发送数据,有必要维护对为每个对等方创建的会话的引用。为此,我一直在使用可变字典。

在新会话发送/接受邀请后,使用MCSession 委托方法更新字典:

- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {

    if (state==MCSessionStateConnected){

        _myPeerSessions[peerID.displayName] = session;

    }
    else if (state==MCSessionStateNotConnected){

        //This is where the session can be disconnected without
        //affecting other peers
        [session disconnect];            

        [_myPeerSessions removeObjectForKey:peerID.displayName];
    }
}

可以使用返回字典所有值的方法访问所有对等点,然后每个MCSession 依次返回所有connectedPeers(在本例中为一个):

- (NSArray *)allConnectedPeers {

   return [[_myPeerSessions allValues] valueForKey:@"connectedPeers"];

}

可以使用以下方法向特定对等方或通过广播发送数据:

- (void)sendData:(NSData *)data toPeerIDs:(NSArray *)remotePeers reliable:(BOOL)reliable error:(NSError *__autoreleasing *)error {

    MCSessionSendDataMode mode = (reliable) ? MCSessionSendDataReliable : MCSessionSendDataUnreliable;

    for (MCPeerID *peer in remotePeers){

       NSError __autoreleasing *currentError = nil;

       MCSession *session = _myPeerSessions[peer.displayName];
       [session sendData:data toPeers:session.connectedPeers withMode:mode error:currentError];

       if (currentError && !error)
        *error = *currentError;
    }
}

【讨论】:

  • 至于框架本身,我同意你@ArjunMehta。除了简单的用例之外,您不能依赖它。它在第一个 Xcode 5 测试版中根本不起作用,从那时起 Apple 几乎没有做任何事情。
  • 我会在那个电话中加入我的声音,它有这样的承诺,但我们不得不等待很长一段时间才能让它发挥作用,然后从那以后它就没有得到太多的爱.
  • 这真是一个令人兴奋的想法!我希望它像想象的那样简单,但这似乎是一件很挑剔的事情。我注意到,如果我在我的设备上禁用蓝牙,它只会稍微可靠一点。但是当其中一个客户端关闭应用程序(甚至不是强制退出)时,它似乎会出错。
  • @ChrisH 有没有一种模式可以用来连接所有这些单一的 1-1 连接,让您感觉像是一个会话?
  • 任何人都可以上传一个具有这种mc连接形式的项目演示吗?
【解决方案2】:

您是否尝试过在应用程序关闭之前断开会话?这应该会正确地从会话中删除对等点并清理为对等点分配的所有资源。

具体来说,我的意思是applicationWillTerminate:中的[self.peer disconnect]

【讨论】:

  • 这个我试过了,是的。理想情况下,我的应用程序将支持后台,因此 applicationWillTerminate 甚至不会被调用。 didEnterBackgroundDoes 被调用,我正在尝试在那里做一些事情。但是,即使我尝试断开对等方与会话的连接,仍然没有运气。我想你的意思可能是 [self.session disconnect]?
  • 我发现大多数时候它确实得到了 MCSessionStateNotConnected。但是,有时某些对等方不会收到通知。即使在客户端上调用断开连接时,我也看到了这一点。
  • 那是一个不同的测试用例。硬退出应用程序也应该可以工作(例如,这类似于超出范围)。
【解决方案3】:

我无法得到接受的答案,所以我所做的是有一个计时器,当浏览器报告未连接并且没有其他连接的对等方时,它会触发以重置连接。

-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{

//DebugLog(@"session didChangeState: %ld",state);

if(resetTimer != nil){
    [resetTimer invalidate];
    resetTimer = nil;
}

if(state == MCSessionStateNotConnected){

    [session disconnect];
    [peerSessions removeObjectForKey:peerID.displayName];
    [self removeGuidyPeerWithPeerID:peerID];
    //DebugLog(@"removing all guides from peer %@",peerID);

    if([localSession connectedPeers].count == 0){

        DebugLog(@"nothing found... maybe restart in 3 seconds");
        dispatch_async(dispatch_get_main_queue(), ^{
            resetTimer = [NSTimer
                      scheduledTimerWithTimeInterval:3.0
                      target:self selector:@selector(onResetTimer:)
                      userInfo:nil
                      repeats:NO];
            }
        );
    }
}
...

}

【讨论】:

    【解决方案4】:

    我也遇到过类似的问题。似乎如果我在一台 iOS 设备上运行我的应用程序并连接到另一台设备,然后退出并重新启动(比如当我从 Xcode 重新运行时),那么我会收到一条已连接的消息,然后是未连接的消息稍后发消息。这让我失望了。但更仔细地看,我可以看到 Not Connected 消息实际上是针对与已连接的 peerId 不同的 peerId。

    我认为这里的问题是我见过的大多数示例只关心 peerID 的 displayName,而忽略了您可以为同一设备/displayName 获取多个 peerID 的事实。

    我现在首先检查 displayName,然后通过比较指针来验证 peerID 是否相同。

    - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
    
        MyPlayer *player = _players[peerID.displayName];
    
        if ((state == MCSessionStateNotConnected) &&
            (peerID != player.peerID)) {
            NSLog(@"remnant connection drop");
            return; // note that I don't care if player is nil, since I don't want to
                    // add a dictionary object for a Not Connecting peer.
        }
        if (player == nil) {
            player = [MyPlayer init];
            player.peerID = peerID;
            _players[peerID.displayName] = player;
        }
        player.state = state;
    
    ...
    

    【讨论】:

    • displayName 对于不同的对等体来说可以是相同的,所以除了显示它之外,你不应该对 displayName 做任何事情。您的 _players 字典应由 peerID 而非 displayName 键入。
    【解决方案5】:

    您可以在 Swift 3 中使用以下代码从 MCBrowserViewController 中删除对等点:

    self.mySession.cancelConnectPeer(self.myPeerID)
    

    【讨论】:

    • 你怎么知道什么时候做这个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多