Подтвердить что ты не робот

Многоуровневая инфраструктура подключения - потерянный партнер находится на сеансе

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

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

  • У меня есть активный MCSession с несколькими одноранговыми узлами.
  • Теперь, если устройство находится в сеансе, а затем принудительно завершает работу, этот "равный" остается подключенным на неопределенный промежуток времени.
  • Я ничего не могу сделать, чтобы отключить этого пользователя, хотя метод browser:lostPeer: вызывается для этого однорангового узла и больше не отображается в браузере как "Ряд".
  • Метод session:peer:didChangeState: не вызывается для этого однорангового узла.
  • Когда этот сверстник покинул эту команду, возвращается в приложение, они снова "Найдены" browser:foundPeer:withDiscoveryInfo:, но все еще существуют в session.connectedPeers NSArray. Очевидно, что они не получают никаких данных или обновлений о сеансе и фактически не подключены.
  • Единственное, что, похоже, работает, чтобы зарегистрировать этот оригинальный peer как MCSessionStateNotConnected для сеанса, путем повторного подключения этого однорангового узла к исходному сеансу. Затем происходит повторный вызов session:peer:didChangeState:, где новый экземпляр peerID равен MCSessionStateConnected и вскоре после старого экземпляра вызовов peerID с MCSessionStateNotConnected.

Пример чат-приложения демонстрирует эту проблему: https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html

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

Эта структура кажется немного беспорядочной, но я пытаюсь зарезервировать решение!

4b9b3361

Ответ 1

Моим единственным обходным решением для этого типа было отношение 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;
    }
}

Ответ 2

Вы пытались отключить сеанс до закрытия приложения? Это должно правильно удалить сверстника из сеанса и очистить все ресурсы, выделенные для однорангового узла.

В частности, я имею в виду что-то вроде [self.peer disconnect] в applicationWillTerminate:

Ответ 3

У меня были подобные проблемы. Кажется, что если я запустил приложение на одном устройстве 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;

...

Ответ 4

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

-(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];
            }
        );
    }
}
...

}

Ответ 5

Вы можете удалить одноранговый узел из MCBrowserViewController со следующим кодом в Swift 3:

self.mySession.cancelConnectPeer(self.myPeerID)