Платформа многорангового подключения — потерянный узел остается в сеансе
-
21-12-2019 - |
Вопрос
Интересно, готова ли эта платформа многорангового подключения к использованию в реальном мире, учитывая все ошибки, с которыми столкнулось сообщество?Я думаю, что настраиваю правильно, но все остальные примеры проектов, которые я пробовал, сталкиваются с аналогичными проблемами.
Проблема, с которой я столкнулся, может быть связана с какой-то проблемой, присущей Bonjour или чему-то еще, я не могу этого понять, но в основном проблема заключается в следующем:
- у меня активный
MCSession
с рядом сверстников. - Теперь, если устройство находится в сеансе, а затем сила уходит, что «Peer» остается на связи в течение неопределенного периода времени.
- Я ничего не могу сделать, чтобы вытеснить этого пользователя, хотя
browser:lostPeer:
Метод требуется для этого сверстника и больше не появляется в браузере как «близлежащий». - А
session:peer:didChangeState:
метод не вызывается для этого узла. - Когда тот узел, который был принудительно закрыт, возвращается в приложение, он снова «находится»
browser:foundPeer:withDiscoveryInfo:
но все еще существуют и вsession.connectedPeers
NSArray.Очевидно, они по-прежнему не получают никаких данных или обновлений о сеансе и фактически не подключены. - Единственное, что, кажется, работает, чтобы зарегистрировать этот исходный узел как
MCSessionStateNotConnected
к сеансу осуществляется путем повторного подключения этого узла к исходному сеансу.Затем происходит дублирующий вызовsession:peer:didChangeState:
где находится новый экземпляр PeerIDMCSessionStateConnected
и вскоре после того, как старый экземпляр вызова PeerID сMCSessionStateNotConnected
.
Пример приложения чата хорошо демонстрирует эту проблему: https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html
Поскольку, похоже, нет никакого способа принудительно удалить однорангового узла из сеанса вручную, что мне делать?Стоит ли мне попытаться как-то перестроить сессию?
Этот Framework кажется немного беспорядочным, но я стараюсь воздерживаться от суждений!
Решение
Моим единственным решением проблемы такого типа было установление отношений 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];
}
}
Доступ ко всем узлам можно получить с помощью метода, который возвращает все значения словаря и, в свою очередь, все connectedPeers
(в данном случае один) для каждого MCSession
:
- (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;
}
}
Другие советы
Пробовали ли вы отключить сеанс до закрытия приложения?Это должно правильно удалить одноранговый узел из сеанса и очистить все ресурсы, выделенные для однорангового узла.
Конкретно я имею в виду что-то вроде [self.peer disconnect]
в applicationWillTerminate:
У меня были подобные проблемы.Однако кажется, что если я запустил свое приложение на одном устройстве iOS и подключился к другому, затем выхожу и перезапускаю (скажем, когда я перезапускаю из Xcode), то я нахожусь в ситуации, когда я получаю сообщение «Подключено», а затем «Не подключено». сообщение чуть позже.Это меня сбило с толку.Но присмотревшись внимательнее, я вижу, что сообщение «Не подключено» на самом деле предназначено для другого 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;
...
Мне не удалось заставить принятый ответ когда-либо работать, поэтому вместо этого я установил таймер, который срабатывал бы для сброса соединения, когда браузер сообщал, что он не подключен и не было других подключенных узлов.
-(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];
}
);
}
}
...
}
Вы можете удалить одноранговый узел из MCBrowserViewController с помощью следующего кода в Swift 3:
self.mySession.cancelConnectPeer(self.myPeerID)