ios - Reconexión a los compañeros desconectados
iphone bluetooth (7)
Estoy usando el framework iOS 7 Multipeer en mi aplicación, pero tengo un problema con la desconexión de los dispositivos. Si abro la aplicación en dos dispositivos: dispositivo A y dispositivo B, los dos dispositivos se conectan entre sí automáticamente. Sin embargo, después de varios segundos, el dispositivo A se desconecta del dispositivo B. Es decir, al principio la conexión es así:
A ---> B
A <--- B
Después de varios segundos:
A ---> B
A B
El dispositivo A mantiene su conexión, pero el dispositivo B es un MCSessionStateNotConnected.
Esto significa que A puede enviar datos a B pero B no puede responder. Intenté evitar esto comprobando si el dispositivo está conectado y, si no lo está, reiniciando la conexión con:
[browser invitePeer:peerID toSession:_session withContext:Nil timeout:10];
Pero la devolución de llamada de didChangeState simplemente se llama con MCSessionStateNotConnected.
Extrañamente, si envío la aplicación A al fondo, luego la vuelvo a abrir, B vuelve a conectarse y la conexión se mantiene.
La API de Multipeer (y la documentación) parece un poco escasa, así que asumí que simplemente funcionaría. En esta situación, ¿cómo debo volver a conectar el dispositivo?
Estaba teniendo el mismo problema, y parece que estaba relacionado con la navegación y la publicidad de mi aplicación al mismo tiempo, y se habían enviado / aceptado dos invitaciones. Cuando dejé de hacer esto y dejé que un compañero se retrasara para invitarlos, los dispositivos permanecieron conectados.
En el delegado de mi navegador, estoy comprobando el valor de hash de la displayName
del compañero descubierto y solo envío una invitación si mi compañero tiene un valor de hash más alto:
Editar
Como señala @Masa, el valor hash
de un NSString
será diferente en los dispositivos de 32 y 64 bits, por lo que es más seguro usar el método compare:
en displayName
.
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {
NSLog(@"Browser found peer ID %@",peerID.displayName);
//displayName is created with [[NSUUID UUID] UUIDString]
BOOL shouldInvite = ([_myPeerID.displayName compare:peerID.displayName]==NSOrderedDescending);
if (shouldInvite){
[browser invitePeer:peerID toSession:_session withContext:nil timeout:1.0];
}
else {
NSLog(@"Not inviting");
}
}
Como dice, la documentación es escasa, por lo tanto, quién sabe lo que Apple realmente quiere que hagamos, pero experimenté con el envío y la aceptación de invitaciones utilizando una sola sesión y también creando una nueva sesión para cada invitación aceptada / enviada, pero esto una forma particular de hacer las cosas me ha dado el mayor éxito.
Este es el resultado de un error, que le informé a Apple. Le expliqué cómo solucionarlo en mi respuesta a otra pregunta: ¿por qué mi par MCSession se desconecta al azar?
No he marcado estas preguntas para fusionar, ya que si bien el error y la solución subyacentes son los mismos, las dos preguntas describen problemas diferentes.
Guarde el hash del par B. Utilizando un temporizador, compruebe el estado de la conexión continuamente si no está conectado intente volver a conectar con cada período de tiempo dado.
Me gustó la solución de ChrisH, que revela la idea clave de que solo un par debe conectarse con el otro , no ambos. Los intentos de conexión mutua dan como resultado una desconexión mutua (aunque no es que una conexión de un solo lado en realidad sea , de manera contraintuitiva, una conexión mutua en términos de estado y comunicación, por lo que funciona bien).
Sin embargo, creo que un enfoque mejor que el de la invitación de un par es que ambos puedan invitar, pero solo un que la acepte . Utilizo este método ahora y funciona muy bien, porque ambos pares tienen la oportunidad de pasar información rica a la otra a través del parámetro de context
de la invitación, en lugar de tener que confiar en la escasa información disponible en el método delegado foundPeer
.
Por lo tanto, recomiendo una solución como tal:
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
{
[self invitePeer:peerID];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL accept, MCSession *session))invitationHandler
{
NSDictionary *hugePackageOfInformation = [NSKeyedUnarchiver unarchiveObjectWithData:context];
BOOL shouldAccept = ([hugePackageOfInformation.UUID.UUIDString compare:self.user.UUID.UUIDString] == NSOrderedDescending);
invitationHandler(shouldAccept && ![self isPeerConnected:peerID], [self openSession]);
}
Para cualquier persona interesada, creé MCSessionP2P , una aplicación de demostración que ilustra las funciones de red ad-hoc de MCSession
. La aplicación se anuncia a sí misma en la red local y se conecta programáticamente a los pares disponibles, estableciendo una red de igual a igual. Sombrero de punta a @ChrisH por su técnica de comparar los valores hash para invitar a sus compañeros.
Según el documento de Apple Elección de un invitador cuando se usa la Conectividad de Multipeer "En iOS 7, el envío de invitaciones simultáneas puede hacer que ambas invitaciones fallen, lo que hace que ambos pares no puedan comunicarse entre sí".
Pero iOS 8 lo ha arreglado.
Tengo el mismo problema cuando los dispositivos intentan conectarse entre sí al mismo tiempo y no sé cómo encontrar una razón porque no tenemos ningún error con MCSessionStateNotConnected .
Podemos usar alguna forma ingeniosa de resolver este problema: poner en registros de txt (información de descubrimiento) una vez [[fecha de fecha de caducidad] tiempo de intervalo de entrada (Fecha de cierre)] cuando se inició la aplicación. Quien comenzó primero - envía invitaciones a otros.
Pero creo que no es una forma correcta (si las aplicaciones comienzan al mismo tiempo, es poco probable ... :)). Tenemos que averiguar la razón.