Почему мой коллега MCSession отключается случайно?

Я использую MCNearbyServiceBrowser и MCNearbyServiceAdvertiser для объединения двух одноранговых узлов в MCSession. Я могу отправлять данные между ними, используя метод sendData MCSession. Кажется, что все работает как ожидалось, пока я не случайно (и не из-за какого-либо события, которое я контролирую) получаю MCSessionStateNotConnected через обработчик MCSessionDelegate sessionChangeState. Кроме того, у массива connectedPeers MCSession больше нет моих сверстников.

Два вопроса: почему? и как я могу отключить MCSession?

Это ошибка, о которой я только что сообщил Apple. Документы утверждают, что didReceiveCertificate вызов didReceiveCertificate является необязательным, но это не так. Добавьте этот метод в свой MCSessionDelegate :

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

Случайные отключения должны прекратиться.

ОБНОВЛЕНИЕ После того, как вы использовали билет поддержки в Apple, они подтвердили, что слишком часто вызывает sendData и слишком много данных может привести к отключению.

У меня были разъединения при попадании точек разрыва и при фоновой настройке. Поскольку точки останова не будут выполняться в магазине приложений, вам необходимо обработать случай фоновой записи, начав фоновое задание, когда ваше приложение вот-вот войдет в фон. Затем завершите эту задачу, когда ваше приложение вернется на передний план. На iOS 7 это дает вам около трех фоновых минут, которые лучше, чем ничего.

Дополнительной страtagsей было бы запланировать локальное уведомление, возможно, за 15 секунд до истечения времени вашего фонового времени, используя [[UIApplication sharedApplication] backgroundTimeRemaining] , таким образом вы можете вернуть пользователя в приложение до его приостановки, а многоуровневая структура должна быть выключенным. Возможно, местное уведомление предупредило бы их, что их сессия истечет через 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 из-за ошибки Apple:

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

Есть много причин этого, и два ответа до сих пор верны в моем опыте. Другой, который вы найдете в других подобных вопросах, таков: только один человек может принять приглашение другого .

Итак, чтобы уточнить, если вы настроите приложение, в котором все устройства являются как рекламодателями, так и браузерами, любые устройства могут свободно приглашать других, которые могут присоединиться к сеансу. Однако между любыми двумя заданными устройствами только одно устройство может фактически принять приглашение и подключиться к другому устройству. Если оба устройства принимают приглашения друг друга, они будут отключены в течение минуты или меньше.

Обратите внимание, что это ограничение не мешает желаемому поведению, потому что, в отличие от того, что моя интуиция заявила до того, как я построила свою реализацию многоузлового объединения – когда одно устройство принимает приглашение и подключается к другому устройству, они оба становятся подключенными и получают методы делегирования соединений и могут отправлять друг другу сообщения ,

Поэтому, если вы подключаете устройства, которые просматривают и рекламируют, отправляйте приглашения свободно, но только принимайте одну из пары .

Проблема только принятия одного из двух приглашений может быть решена множеством способов. Для начала поймите, что вы можете передать любой произвольный объект или словарь (заархивированные как данные) в качестве аргумента context в приглашении. Поэтому оба устройства имеют доступ к любой произвольной информации о другой (и, конечно же, о себе). Таким образом, вы можете использовать, по крайней мере, следующие страtagsи:

  • просто compare: отображаемое имя peerID. Но нет никакой гарантии, что они не будут равными.
  • сохраните дату, когда ваш controller мультиплеера был инициализирован, и используйте это для сравнения
  • дайте каждому партнеру UUID и отправьте это для сравнения (моя техника, в которой каждое устройство – действительно, каждый пользователь приложения на устройстве – имеет постоянный UUID, который он использует).
  • и т. д. – любой объект, который поддерживает как NSCoding, так и compare: будет хорошо.

У меня были подобные проблемы. Кажется, что если я запустил приложение на одном устройстве iOS и подключился к другому, то закройте и перезапустите (скажем, когда я перейду с Xcode), то я в ситуации, когда я получаю сообщение Connected, а затем не подключен сообщение немного позже. Это меня отбросило. Но, глядя более внимательно, я вижу, что сообщение Not Connected на самом деле предназначено для другого peerId, чем тот, который подключен.

Я думаю, проблема в том, что большинство образцов, которые я видел, просто заботятся о displayName peerID и пренебрегают тем фактом, что вы можете получить несколько peerID для одного и того же устройства / displayName.

Теперь я сначала проверяю 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; ... 

Я сразу отключился после того, как я принял запрос на подключение. Наблюдая за состоянием, я увидел, что он изменился с 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); } 

и отсоединения прекратились.

  • CALayer: добавить границу только с одной стороны
  • В чем разница между «отображаемым именем пакета» и «именем пакета» в информационном plist приложения cocoa
  • Core-Data iPhone: не удалось найти NSManagedObjectModel
  • Воспроизведение музыки в фоновом режиме с помощью AVAudioplayer
  • objective C - Как использовать метод initWithCoder?
  • Воспроизведение звука на iPhone даже в бесшумном режиме
  • Сравнение двух NSDates и игнорирование временной составляющей
  • Сортировка NSArray пользовательских объектов по их свойствам NSDate
  • Лучший способ сохранить данные на iPhone
  • Могу ли я отключить прокрутку звука UIPickerView?
  • Только два закругленных угла?
  • Давайте будем гением компьютера.