Платформа многорангового подключения — потерянный узел остается в сеансе

StackOverflow https://stackoverflow.com//questions/23014523

Вопрос

Интересно, готова ли эта платформа многорангового подключения к использованию в реальном мире, учитывая все ошибки, с которыми столкнулось сообщество?Я думаю, что настраиваю правильно, но все остальные примеры проектов, которые я пробовал, сталкиваются с аналогичными проблемами.

Проблема, с которой я столкнулся, может быть связана с какой-то проблемой, присущей Bonjour или чему-то еще, я не могу этого понять, но в основном проблема заключается в следующем:

  • у меня активный MCSession с рядом сверстников.
  • Теперь, если устройство находится в сеансе, а затем сила уходит, что «Peer» остается на связи в течение неопределенного периода времени.
  • Я ничего не могу сделать, чтобы вытеснить этого пользователя, хотя browser:lostPeer: Метод требуется для этого сверстника и больше не появляется в браузере как «близлежащий».
  • А session:peer:didChangeState: метод не вызывается для этого узла.
  • Когда тот узел, который был принудительно закрыт, возвращается в приложение, он снова «находится» browser:foundPeer:withDiscoveryInfo: но все еще существуют и в session.connectedPeers NSArray.Очевидно, они по-прежнему не получают никаких данных или обновлений о сеансе и фактически не подключены.
  • Единственное, что, кажется, работает, чтобы зарегистрировать этот исходный узел как MCSessionStateNotConnected к сеансу осуществляется путем повторного подключения этого узла к исходному сеансу.Затем происходит дублирующий вызов session:peer:didChangeState: где находится новый экземпляр PeerID MCSessionStateConnected и вскоре после того, как старый экземпляр вызова 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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top