为什么我的MCSession节点随机断开?

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

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

这是我刚刚向苹果报告的一个错误。 docs声称didReceiveCertificatecallback是可选的,但事实并非如此。 将此方法添加到您的MCSessionDelegate

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

随机断线应该停止。

更新使用支持票到苹果后,他们证实,经常调用sendData和太多的数据可能会导致断开连接。

遇到断点和背景时,我已经断开连接。 由于应用程序商店不会发生中断点,所以当您的应用程序即将进入后台时,您需要通过开始后台任务来处理后台情况。 然后在您的应用程序回到前台时结束此任务。 在iOS 7上,这给了你大约3分钟的背景,比没有好。

另外一个策略是在背景时间到期之前,通过使用[[UIApplication sharedApplication] backgroundTimeRemaining]来安排一个本地通知大概15秒钟,这样你就可以在用户暂停之前把用户带回到应用程序中,并且多对等框架必须被closures。 也许当地的通知会警告他们,他们的会话将在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 } 

由于Apple错误,您还需要在MCSession委托中使用此处理程序:

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

造成这种情况的原因有很多,迄今为止的两个答案在我的经验中都是正确的。 另一个你会发现在其他类似的问题是这样的: 只有一个同行可以接受别人的邀请

因此,为了澄清,如果您设置了一个应用程序,其中所有设备都是广告客户和浏览器,则任何设备都可以自由邀请任何发现的其他人join会话。 但是,在任何两个给定的设备之间,只有一个设备可以实际接受邀请并连接到另一个设备。 如果两个设备接受彼此的邀请,他们将在一分钟或更短的时间内断开连接。

请注意,这个限制并不妨碍所需的行为,因为 – 不同于我build立我的multipeer实现之前所陈述的我的直觉 – 当一个设备接受邀请并连接到另一个设备,他们成为连接和接收连接委托方法,并可以发送对方的消息。

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

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

  • 简单地compare: peerID的显示名称。 但是不能保证这些不会是平等的。
  • 存储您的多路控制器初始化的date,并将其用于比较
  • 给每个对端一个UUID,并发送这个比较(我的技术,其中每个设备 – 实际上是一个设备上的应用程序的每个用户)具有它使用的持久UUID。
  • 任何支持NSCoding和compare:对象都可以。

我一直有类似的问题。 似乎如果我已经在一台iOS设备上运行我的应用程序,并连接到另一台,然后退出并重新启动(比如说,当我从Xcode重新运行的时候),那么我处于一种情况,在那里我得到一个连接消息,然后是一个不连接稍后留言。 这是把我扔掉。 但仔细观察,我可以看到“未连接”消息实际上是指与已连接的不同peerId。

我认为这里的问题是,我所看到的大多数示例只关心peerID的displayName,忽略了可以为同一个device / displayName获取多个peerID的事实。

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

 - (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; ... 

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

我正在创build我的会议:

 [[MCSession alloc] initWithPeer:peerID] 

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

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

基于Andrew的技巧,我添加了委托方法

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

并断开连接。