2013-09-21 49 views
38

进出口使用MCNearbyServiceBrowser和MCNearbyServiceAdvertiser连接两个同伴到MCSession。我可以使用MCSession的sendData方法在它们之间发送数据。所有似乎按预期工作,直到我随机(而不是由于我控制的任何事件)通过会话的MCSessionDelegate didChangeState处理程序接收MCSessionStateNotConnected。另外,MCSession的connectedPeers数组不再拥有我的对等体。为什么我的MCSession对等体会随机断开连接?

两个问题:为什么?和我如何保持MCSession断开连接?

+0

我有同样的问题,但我有一些数据发送后断开连接。你有解决这个问题吗? – Moonkid

+0

我注意到的一件事是在调试器中暂停MCSession。我最终编写了一个机制来重新建立会话,如果它被丢弃。 – tillerstarr

+0

我有同样的问题。我注意到,如果一个设备是背景灯,并且消息被发送给它,则会发生断开连接。 – jjxtra

回答

17

UPDATE在使用Apple支持凭单后,他们确认经常调用sendData并且数据太多会导致断开连接。

击球破发点并backgrounding时,当我有断开。由于应用商店不会发生断点,因此您需要在应用即将进入后台时开始后台任务来处理后台情况。然后在您的应用回到前台时结束此任务。在iOS 7上,这给了你大约3分钟的背景,比没有好。

另一个策略是在背景时间到期前使用[[UIApplication sharedApplication] backgroundTimeRemaining]安排本地通知大概15秒钟,这样您可以在挂起应用程序之前将用户带回应用程序,并且必须关闭多对等框架。也许本地通知会警告他们,他们的会话将在10秒内过期...

如果后台任务到期并且应用程序仍在后台,则必须拆除与多点对等连接相关的所有内容,否则你会碰到崩溃。

- (void) createExpireNotification 
{ 
    [self killExpireNotification]; 

    if (self.connectedPeerCount != 0) // if peers connected, setup kill switch 
    { 
     NSTimeInterval gracePeriod = 20.0f; 

     // create notification that will get the user back into the app when the background process time is about to expire 
     NSTimeInterval msgTime = UIApplication.sharedApplication.backgroundTimeRemaining - gracePeriod; 
     UILocalNotification* n = [[UILocalNotification alloc] init]; 
     self.expireNotification = n; 
     self.expireNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:msgTime]; 
     self.expireNotification.alertBody = TR(@"Text_MultiPeerIsAboutToExpire"); 
     self.expireNotification.soundName = UILocalNotificationDefaultSoundName; 
     self.expireNotification.applicationIconBadgeNumber = 1; 

     [UIApplication.sharedApplication scheduleLocalNotification:self.expireNotification]; 
    } 
} 

- (void) killExpireNotification 
{ 
    if (self.expireNotification != nil) 
    { 
     [UIApplication.sharedApplication cancelLocalNotification:self.expireNotification]; 
     self.expireNotification = nil; 
    } 
} 

- (void) applicationWillEnterBackground 
{ 
    self.taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ 
    { 
     [self shutdownMultiPeerStuff]; 
     [[UIApplication sharedApplication] endBackgroundTask:self.taskId]; 
     self.taskId = UIBackgroundTaskInvalid; 
    }]; 
    [self createExpireNotification]; 
} 

- (void) applicationWillEnterForeground 
{ 
    [self killExpireNotification]; 
    if (self.taskId != UIBackgroundTaskInvalid) 
    { 
     [[UIApplication sharedApplication] endBackgroundTask:self.taskId]; 
     self.taskId = UIBackgroundTaskInvalid; 
    } 
} 

- (void) applicationWillTerminate 
{ 
    [self killExpireNotification]; 
    [self stop]; // shutdown multi-peer 
} 

您也想这个处理程序在你的MCSession代表由于苹果的bug:

- (void) session:(MCSession*)session didReceiveCertificate:(NSArray*)certificate fromPeer:(MCPeerID*)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler 
{ 
    if (certificateHandler != nil) { certificateHandler(YES); } 
} 
+0

背景时,您可以在将应用程序从后台恢复时保持连接吗? – Tim

+0

只要您的背景时间没有用完,您就可以使用。如果是这样,你别无选择,只能杀死会议或崩溃。 – jjxtra

+0

@PsychoDad在你遇到苹果DTS的时候,你是否经常在被认为调用sendData的东西上学到任何东西? –

25

这是一个错误,这是我刚刚报道了苹果。该文档声称didReceiveCertificate回调是可选的,但事实并非如此。这种方法添加到您的MCSessionDelegate

- (void) session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler 
{ 
    certificateHandler(YES); 
} 

随机断开应该停止。

+7

即使在执行此操作后仍会断开连接 – jjxtra

+1

对于我来说,也不会为它修复(在swift中) – berliner

+1

如果您已收到带有MCSessionStateConnected值的didChangeState消息并且可以成功发送数据,那么您已经完成一个连接,这不是你的问题。其他答案之一可能有更多的信息。然而,如果你(像我一样)对你为什么试图接受连接感到困惑,你会看到MCSessionStateConnecting,然后在几秒钟后看到MCSessionStateNotConnected而没有其他解释,那么这里给出的答案很可能就是你的问题。 – GrandOpener

11

这方面有很多的原因,这两个答案迄今都是以我的经验都正确。另外,你会在其他类似的问题,发现是这样的:只有一个节点可以接受别人的邀请

所以,澄清,如果你设置了一个应用程序,所有设备均是广告商和浏览器,任何设备可以随意邀请任何人发现加入会话。但是,在任何两个给定设备之间,只有一个设备可以实际接受邀请并连接到其他设备。如果两台设备接受彼此的邀请,他们将在一分钟或更短时间内断开连接。

请注意,此限制并不妨碍所需的行为,因为 - 不像我的直觉所述,在构建我的多路复用器实现之前 - 当一个设备接受邀请并连接到另一设备时,它们都会连接并接收连接委托方法并可以发送对方的消息。

因此,如果您连接的设备既浏览和广告,自由发送邀请,但只接受一对其中之一。

只接受两个邀请之一的问题可以通过无数种方式解决。首先,要明白,您可以在邀请中传递任意任意对象或字典(存档为数据)作为context参数。因此,这两个设备都可以访问任何关于其他设备的信息(当然也可以)。因此,您至少可以使用以下策略:

  • 只是compare: peerID的显示名称。但是不能保证这些不会是平等的。
  • 存储区multipeer控制器被初始化,并利用它来进行比较
  • 给每个同行一个UUID,并将其发送比较(我的技术,其中每个设备的日期 - 设备上的应用程序确实每个用户 - 拥有它使用的持续UUID)。
  • 等 - 任何支持NSCoding和compare:的对象都可以。
+0

我认为这是我的问题。所以我甚至编造了一个令人费解的延迟邀请,但是他们都还没有连接。 – daidai

+1

这实际上是这个问题:'请注意:模拟器不支持蓝牙网络.' – daidai

1

我在接受连接请求后立即断开连接。观察状态,我看到它从MCSessionStateConnected更改为MCSessionStateNotConnected。

我创造我的会议有:

[[MCSession alloc] initWithPeer:peerID] 

不实例化方法处理安全证书:

- (instancetype)initWithPeer:(MCPeerID *)myPeerID securityIdentity:(NSArray *)identity encryptionPreference:(MCEncryptionPreference)encryptionPreference 

基于安德鲁的顶端的上方,我添加了委托方法

- (void) session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler { 
     certificateHandler(YES); 
    } 

并断开连接。

3

我一直有类似的问题。但似乎如果我已经在一台iOS设备上运行我的应用程序并连接到另一台设备,然后退出并重新启动(例如,当我从Xcode重新运行时),那么我处于一种情况,在该情况下,我会收到连接消息,然后是未连接稍后留言。这让我失望了。但仔细观察,我可以看到“未连接”消息实际上是指与已连接的不同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; 

... 
+0

这仍然是相关的,在Swift3上2年! – timothykc

相关问题