Marco de conectividad multipar: el par perdido permanece en la sesión
-
21-12-2019 - |
Pregunta
Me pregunto si este marco de conectividad multipar está listo para usarse en el mundo real, dados todos los errores que ha encontrado la comunidad.Creo que lo estoy configurando correctamente, pero todos los demás proyectos de muestra que probé encuentran problemas similares.
El problema que tengo puede estar relacionado con algún problema inherente a Bonjour o algo así, no puedo entenderlo, pero básicamente el problema es el siguiente:
- tengo un activo
MCSession
con varios compañeros. - Ahora, si un dispositivo está en una sesión, y luego la fuerza se retira, ese "Peer" permanece conectado por un período de tiempo indefinido.
- No hay nada que pueda hacer para expulsar a ese usuario, aunque el
browser:lostPeer:
Se requiere un método para ese par y ya no aparece en el navegador como "cerca". - El
session:peer:didChangeState:
El método no se llama para ese par. - Cuando ese compañero que abandonó a la fuerza regresa a la aplicación, el usuario lo "encontrará" nuevamente.
browser:foundPeer:withDiscoveryInfo:
pero todavía existen en elsession.connectedPeers
NSArray.Obviamente, todavía no reciben ningún dato o actualización sobre la sesión y en realidad no están conectados. - Lo único que parece funcionar para registrar a ese par original como
MCSessionStateNotConnected
a la sesión es reconectando ese par a la sesión original.Luego hay una llamada duplicada asession:peer:didChangeState:
donde está la nueva instancia del peerIDMCSessionStateConnected
y poco después la antigua instancia de las llamadas peerID conMCSessionStateNotConnected
.
La aplicación de chat de muestra demuestra bien este problema: https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Introduction/Intro.html
Dado que no parece haber ninguna forma de forzar manualmente la eliminación de un par de la sesión, ¿qué debo hacer?¿Debería intentar reconstruir la sesión de alguna manera?
Este Marco parece un poco desordenado, ¡pero estoy tratando de reservarme mi opinión!
Solución
Mi única solución a este tipo de problema ha sido tener una relación 1-1 entre las sesiones y los pares.Complica el envío de transmisiones, pero al menos permite desconexiones y limpieza a nivel de pares desconectando/eliminando la sesión misma.
Actualizar
Para ampliar mi respuesta original, para poder enviar datos a pares conectados es necesario mantener una referencia a la sesión que se creó para cada par.He estado usando un diccionario mutable para esto.
Una vez enviada/aceptada la invitación con una nueva sesión, utilice el MCSession
método delegado para actualizar el diccionario:
- (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];
}
}
Se puede acceder a todos los pares con un método que devuelve todos los valores del diccionario y, a su vez, todos connectedPeers
(en este caso uno) para cada MCSession
:
- (NSArray *)allConnectedPeers {
return [[_myPeerSessions allValues] valueForKey:@"connectedPeers"];
}
El envío de datos a un par en particular o mediante transmisión se puede realizar con un 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;
}
}
Otros consejos
¿Has probado a desconectar la sesión antes de que se cierre la aplicación?Esto debería eliminar al par de la sesión correctamente y limpiar cualquier recurso asignado al par.
Específicamente me refiero a algo como [self.peer disconnect]
en applicationWillTerminate:
He estado teniendo problemas similares.Sin embargo, parece que si ejecuté mi aplicación en un dispositivo iOS y me conecté a otro, luego la salgo y la reinicio (por ejemplo, cuando vuelvo a ejecutar desde Xcode), entonces estoy en una situación en la que aparece un mensaje Conectado y luego un mensaje No conectado. mensaje un poco más tarde.Esto me estaba desconcertando.Pero mirando más detenidamente, puedo ver que el mensaje No conectado en realidad está destinado a un peerId diferente al que se ha conectado.
Creo que el problema aquí es que la mayoría de las muestras que he visto solo se preocupan por el nombre para mostrar del peerID y descuidan el hecho de que puede obtener múltiples peerID para el mismo dispositivo/nombre para mostrar.
Ahora estoy verificando primero el nombre para mostrar y luego verificando que el peerID sea el mismo, haciendo una comparación de los punteros.
- (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;
...
No pude lograr que la respuesta aceptada funcionara, así que lo que hice fue tener un temporizador que se activaría para restablecer la conexión cuando el navegador informara que no estaba conectado y no había otros 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];
}
);
}
}
...
}
Puede eliminar el par de MCBrowserViewController con el siguiente código en Swift 3:
self.mySession.cancelConnectPeer(self.myPeerID)