Pergunta

Gostaria de saber se este framework Multipeer Connectivity está pronto para uso no mundo real, dados todos os bugs que foram encontrados pela comunidade.Acho que estou configurando corretamente, mas todos os outros projetos de amostra que tentei encontraram problemas semelhantes.

O problema que estou tendo pode estar ligado a algum problema inerente ao Bonjour ou algo assim, não consigo entender, mas basicamente o problema é o seguinte:

  • Eu tenho um ativo MCSession com vários pares.
  • Agora, se um dispositivo estiver em uma sessão, e então força sai, esse "Peer" permanece conectado por um período de tempo indefinido.
  • Não há nada que eu possa fazer para forçar a saída desse usuário, mesmo que o browser:lostPeer: O método é chamado para esse par e nem sequer aparece no navegador como "próximo".
  • O session:peer:didChangeState: O método não é chamado para esse par.
  • Quando aquele par que forçou o encerramento volta ao aplicativo, ele é "Encontrado" novamente pelo browser:foundPeer:withDiscoveryInfo: mas ainda existem também no session.connectedPeers NSArray.Obviamente eles ainda não recebem nenhum dado ou atualização sobre a sessão e não estão realmente conectados.
  • A única coisa que parece funcionar para registrar esse par original como MCSessionStateNotConnected à sessão é reconectando esse peer à sessão original.Então há uma chamada duplicada para session:peer:didChangeState: onde a nova instância do peerID é MCSessionStateConnected e logo após a instância antiga do peerID chamar com MCSessionStateNotConnected.

O exemplo de aplicativo de bate-papo demonstra bem esse problema: https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html

Como não parece haver nenhuma maneira de forçar manualmente a remoção de um par da sessão, o que devo fazer?Devo tentar reconstruir a sessão de alguma forma?

Esta estrutura parece uma bagunça, mas estou tentando reservar o julgamento!

Foi útil?

Solução

Minha única solução alternativa para esse tipo de problema foi ter um relacionamento 1-1 entre sessões e pares.Isso complica o envio de transmissões, mas pelo menos permite desconexões e limpeza em nível de peer por meio da desconexão/remoção da própria sessão.

Atualizar

Para elaborar minha resposta original, para poder enviar dados aos pares conectados é necessário manter uma referência à sessão que foi criada para cada par.Tenho usado um dicionário mutável para isso.

Assim que o convite for enviado/aceito com uma nova sessão, use o MCSession método delegado para atualizar o dicionário:

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

Todos os pares podem ser acessados ​​com um método que retorna todos os valores do dicionário e, por sua vez, todos connectedPeers (neste caso, um) para cada MCSession:

- (NSArray *)allConnectedPeers {

   return [[_myPeerSessions allValues] valueForKey:@"connectedPeers"];

}

O envio de dados para um determinado peer ou via broadcast pode ser feito com um método como este:

- (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;
    }
}

Outras dicas

Você já tentou desconectar a sessão antes de o aplicativo fechar?Isso deve remover o par da sessão de maneira adequada e limpar todos os recursos alocados para o par.

Especificamente, quero dizer algo como [self.peer disconnect] em applicationWillTerminate:

Tenho tido problemas semelhantes.Parece, porém, que se eu executei meu aplicativo em um dispositivo iOS e me conectei a outro, saia e reinicie (por exemplo, quando executo novamente no Xcode), estou em uma situação em que recebo uma mensagem Conectado e, em seguida, um Não conectado mensagem um pouco mais tarde.Isso estava me confundindo.Mas olhando com mais atenção, posso ver que a mensagem Not Connected se destina, na verdade, a um peerId diferente daquele que foi conectado.

Acho que o problema aqui é que a maioria dos exemplos que vi apenas se preocupa com o displayName do peerID e negligencia o fato de que você pode obter vários peerIDs para o mesmo dispositivo/displayName.

Agora estou verificando primeiro o displayName e depois verificando se o peerID é o mesmo, fazendo uma comparação dos ponteiros.

- (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;

...

Não consegui que a resposta aceita funcionasse, então o que fiz foi ter um cronômetro que seria acionado para redefinir a conexão quando o navegador relatasse que não estava conectado e não havia outros pares conectados.

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

}

Você pode excluir o peer do MCBrowserViewController com o seguinte código em Swift 3:

self.mySession.cancelConnectPeer(self.myPeerID)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top