Повторное подключение к отключенным одноранговым серверам
Я использую инфраструктуру iOS 7 Multipeer в своем приложении, но у меня возникла проблема с отключением устройств. Если я открою приложение на двух устройствах: устройство A и устройство B, два устройства автоматически подключаются друг к другу. Однако через несколько секунд устройство А отсоединяется от устройства В. Вначале соединение выглядит следующим образом:
A ---> B
A <--- B
Через несколько секунд:
A ---> B
A B
Устройство A поддерживает это соединение, но устройство B получает MCSessionStateNotConnected.
Это означает, что A может отправлять данные в B, но B не может ответить. Я попытался обойти это, проверив, подключено ли устройство, а если нет, повторное инициирование соединения с помощью:
[browser invitePeer:peerID toSession:_session withContext:Nil timeout:10];
Но callback didChangeState просто вызывается с помощью MCSessionStateNotConnected.
Странно, если я отправлю приложение A в фоновый режим, затем снова откройте его, B снова подключится к нему и соединение будет поддерживаться.
API-интерфейс Multipeer (и документация) выглядит немного редким, поэтому я предполагал, что он будет работать. В этой ситуации как я должен повторно подключить устройство?
Ответы
Ответ 1
У меня была такая же проблема, и она, похоже, была связана с моим просмотром приложений и рекламой в одно и то же время, и два приглашения были отправлены/приняты. Когда я прекратил это делать и позвольте одному партнеру отложить к другому для приглашений, устройства остались подключенными.
В моем делегате браузера я проверяю хэш-значение обнаруженного peer displayName
и отправляет только приглашение, если мой партнер имеет более высокое значение хэша:
Edit
Как указано @Masa, значение hash
для NSString
будет отличаться на 32 и 64-битных устройствах, поэтому безопаснее использовать метод compare:
на displayName
.
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {
NSLog(@"Browser found peer ID %@",peerID.displayName);
//displayName is created with [[NSUUID UUID] UUIDString]
BOOL shouldInvite = ([_myPeerID.displayName compare:peerID.displayName]==NSOrderedDescending);
if (shouldInvite)
[browser invitePeer:peerID toSession:_session withContext:nil timeout:1.0];
else
NSLog(@"Not inviting");
}
Как вы говорите, документация разрежена, поэтому кто знает, что Apple действительно хочет от нас, но я экспериментировал как с отправкой, так и с приглашением, используя один сеанс, а также для создания нового сеанса для каждого принятого/отправленного приглашения, но этот особый способ делать вещи дал мне наибольший успех.
Ответ 2
Для всех, кого это интересует, я создал MCSessionP2P, демонстрационное приложение, которое иллюстрирует специальные сетевые функции MCSession
. Приложение обе рекламирует себя в локальной сети и программно соединяется с доступными одноранговыми узлами, устанавливая одноранговую сеть. Hat tip для @ChrisH для его метода сравнения значений хэша для приглашения сверстников.
Ответ 3
Мне понравилось решение ChrisH, в котором раскрывается ключевое понимание того, что только один партнер должен подключиться к другому партнеру, а не к обоим. Попытки взаимного соединения приводят к взаимному отключению (хотя не то, что одностороннее соединение действительно , контр-интуитивно, взаимное соединение с точки зрения статуса и связи, так что это нормально).
Тем не менее, я думаю, что лучший подход, чем одно приглашение для одноранговых сетей, предназначен для приглашения обоих сверстников, но только для одного партнера. Я использую этот метод сейчас, и он отлично работает, потому что оба одноранговых узла имеют возможность передавать богатую информацию другому через параметр context
приглашения, а не полагаться на скудную информацию, доступную в методе делегирования foundPeer
.
Поэтому я рекомендую такое решение:
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
{
[self invitePeer:peerID];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL accept, MCSession *session))invitationHandler
{
NSDictionary *hugePackageOfInformation = [NSKeyedUnarchiver unarchiveObjectWithData:context];
BOOL shouldAccept = ([hugePackageOfInformation.UUID.UUIDString compare:self.user.UUID.UUIDString] == NSOrderedDescending);
invitationHandler(shouldAccept && ![self isPeerConnected:peerID], [self openSession]);
}
Ответ 4
У меня такая же проблема, когда устройства пытаются подключиться друг к другу в одно и то же время, и я не знаю, как найти причину, потому что у нас нет ошибок с MCSessionStateNotConnected.
Мы можем использовать какой-то хитрый способ решить эту проблему:
Поместите в txt записи (информацию открытия) время [[NSDate date] timeIntervalSince1970], когда приложение запустилось. Кто начал сначала - отправьте приглашение другим.
Но я думаю, что это не правильный путь (если приложения запускаются в то же время, маловероятно...:)). Нам нужно выяснить причину.
Ответ 5
Это результат ошибки, о которой я сообщил Apple. Я объяснил, как исправить это в ответе на другой вопрос: Почему мой MCSession одноранговый доступ отключен случайно?
Я не задавал эти вопросы для слияния, потому что в то время как основная ошибка и решение одинаковы, два вопроса описывают разные проблемы.
Ответ 6
Сохранение хэша сверстника B. Используя таймер, проверьте состояние соединения непрерывно, если он не подключен, попытайтесь повторно подключиться с каждым заданным периодом времени.
Ответ 7
Согласно документу apple Выбор приглашающего при использовании Multipeer Connectivity
"В iOS 7 отправка одновременных приглашений может привести к сбою обоих приглашений, при этом оба одноранговых узла не смогут общаться друг с другом".
Но iOS 8 исправил его.