Estado de la connection del monitor y event handling gotas

He implementado soluciones de trabajo, ve mi comentario a continuación.

Hola, y gracias por tomarse el time para leer esta publicación.

Estoy desarrollando una aplicación que se conecta a un dispositivo de hardware que emite su propia networking WiFi AdHoc. Soy capaz de conectarme al dispositivo, enviar bytes y recibir bytes a través de un puente directo CFNetwork con NSStream. Estoy usando el código de apertura de flujo bastante "de-facto" y el delegado de flujo informa NSStreamEvents como debería.

Al inicializar mi connection al dispositivo, puedo ver las secuencias de input y salida abiertas (NSStreamEventOpenCompleted) y como el dispositivo de hardware envía constantemente "HELLO!", Inmediatamente hay BytesAvailable en el inputStream (NSStreamEventHasBytesAvailable).

En el caso de NSStreamEventHasBytesAvailable, leo datos de inputStream y lo logging como tal:

case NSStreamEventHasBytesAvailable: NSLog(@"CASE LOG -- NSStreamEventHasBytesAvailable"); uint8_t buffer[256]; int len; while ([inputStream hasBytesAvailable]) { //NSLog(@"LOG -- inputStream hasBytesAvailable"); len = [inputStream read:buffer maxLength:sizeof(buffer)]; if (len > 0) { NSLog(@"Length of inputStream Bytes -- %i",len); NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; // This global boolean affects the rest of the apps functionality. Buttons are not able to send bytes to the hardware device if this boolean is FALSE. deviceIsConnected = true; // If buttons to send bytes are disabled due to lack of network connection on appLaunch, then go ahead and show them, allowing the user to send bytes to the hardware device if(buttonsAreDisabled == true) { [ self setButtonVisibility:true ]; // Reset the status of the "No Connection" Alert connectionAlertShown = false; } // Log the incoming data if (nil != output) { NSLog(@"LOG -- device said: %@", output); } } } break; 

Como era de esperar, tengo un flujo constante de "LOG – device said: xxxx" mientras mi dispositivo está conectado. Sin embargo, si desconecto el dispositivo de su fuente de alimentación, no recibo ningún tipo de Evento de Stream; el logging simplemente se detiene todos juntos.

He intentado remediar este problema al iniciar un backgroundTimer en mi viewDidLoad, que cada 0.1 segundos intenta leer desde el inputStream. Si no puede leer, entonces el deviceIsConnected boolean se deviceIsConnected a FALSE y deviceIsConnected una alerta informando al usuario que su connection con el dispositivo se ha caído.

Este método ha demostrado ser poco confiable, y también una manera muy desagradable de realizar una tarea aparentemente simple de detectar el cierre de una connection de socket. Si entiendo correctamente, la class NSStream es esencialmente una capa de "hombre medio" o de abstracción sobre la BSD Socket Architecture subyacente.

Al desconectar el dispositivo de hardware de su fuente de alimentación, se simula salir del range del chip WiFi incorporado del dispositivo. Esta no es una testing de "mundo real", como si se estuviera alejando físicamente del dispositivo, no perderá de pronto la connection; más bien, los datos recibidos por el inputStream se deteriorarán lentamente, lo que provocará que la window emergente "Alerta de networking" parpadee continuamente a medida que el dispositivo salta entre "conectado" y "no conectado".

Me gustaría implementar algún tipo de controller KeepAlive, pero mi falta de experiencia con iPhone / iOS / BSD Sockets me está dificultando severamente. Si alguno de ustedes, individuos maravillosos, pudiera proporcionar un ejemplo básico de un método (probablemente funcionando con un timer, ¡creo que estoy en el path correcto!) Que puede detectar que un socket no está disponible y proceder a intentar restablecer la connection, estaría eternamente agradecido. He buscado incansablemente en Google y he encontrado algunas ideas prometedoras, pero no he podido implementarlas con éxito.

¿CocoaASyncSocket podría ser la respuesta a todas mis preguntas / frustraciones?

Gracias nuevamente por tomarse el time para leer esta publicación. Espero haber proporcionado una explicación clara de mi problema y mi solución deseada. Si tiene alguna pregunta, no dude en preguntar y haré todo lo posible para contestar.

Mi teoría previamente explicada (ver comentarios) realmente ha funcionado. Ahora puedo monitorear con éxito el estado de la conectividad del dispositivo con la siguiente lógica (por favor comprenda que los siguientes fragments de código existen en toda la aplicación). Puedo determinar el momento exacto en que el dispositivo no está disponible; ya sea debido a la falta de conectividad WiFi, o al dispositivo que pierde potencia.

 // Define two socket objects, a "Main" socket and "Observer" socket asyncSocketMain = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; dispatch_queue_t secondaryQueue = dispatch_get_current_queue(); asyncSocketObserver = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:secondaryQueue]; // On application launch, attempt to open asyncSocketMain [asyncSocketMain connectToHost:host onPort:port withTimeout: 2.0 error:&error] // Determine which socket object is connecting in the didConnectToHost method - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port { NSString *currentSocketId = (NSString *)sock; // Determine Which Socket Object is Connecting... if(currentSocketId == (NSString *)asyncSocketMain) { NSLog(@"Main Socket has Connected!"); connectionIsOpening = false; // Allow for Future Reconnect Attempts deviceIsConnected = true; // Allow Connection Monitoring // If the Main Socket has been attempting to reconnect, stop doing that! if(reconnectTimer) { [ reconnectTimer invalidate ]; // Stop the reconnectTimer reconnectTimer = nil; // And also set its value to nil } [ self setupMonitorTimer ]; // Begin Monitoring the Connection }else{ if(currentSocketId == (NSString *)asyncSocketObserver) { NSLog(@"Observer Socket attempting connection! Socket: %@", sock); }else{ NSLog(@"ALERT ALERT -- UNKNOWN SOCKET CONNECTING!!!"); } } } // close void 

Ahora, cuando el socket del observador intenta conectarse, se lanzará un error. Así es como puedo determinar la conectividad actual. El observador siempre arrojará el código de error 7 si el socket asyncSocketMain ya está conectado. Si asyncSocketObserver time de espera al intentar conectarse, significa que el dispositivo está apagado, fuera de scope o no disponible (por ejemplo, el teléfono del usuario no está conectado a la networking WiFi correcta). En este caso, todo "monitoreo" debe detenerse y se inicia un timer para asyncSocketMain para intentar reconectarse.

 - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err { NSString *connectingSocket = (NSString *)sock; // Figure out hte Error Code Info NSError * error; error = err; NSInteger errorNum = error.code; if(connectingSocket == (NSString *)asyncSocketMain) { // This may occur if the app is opened when the device is out of range NSLog(@"The MAIN SOCKET Encountenetworking an Error while Connecting! [ CODE: %d ]", errorNum); [ asyncSocketMain disconnect ]; // Disconnect the Main Socket to allow for reconnects connectionIsOpening = false; // Allow Main Connection Attempts deviceIsConnected = false; // Device is NOT CONNECTED -- Do NOT RUN MONITORING if(!reconnectTimer) { NSLog(@"Starting the reconnectTimer"); [ self setupReconnectTimer ]; // Start attempting to reconnect }else{ NSLog(@"Reconnect Timer is Already Running!"); } } else if(connectingSocket == (NSString *)asyncSocketObserver) { switch (errorNum) { case 1: // Not much to do here... NSLog(@"OBSERVER ERROR - There is already a socket attempting to connect!"); break; case 2: // Not much to do here... NSLog(@"OBSERVER ERROR - Event 2"); break; case 3: // Time Out -- The device is out of range. Halt observer connection attempts, disconnect the main // socket object, then proceed to attempt to reconnect with the main socket. NSLog(@"OBSERVER ERROR - Connected Timed out -- Device not available!!!!"); // The Observer Socket Timed out -- It's time to start reconnecting connectionIsOpening = false; // Allow Main Connection Attempts deviceIsConnected = false; // Device is NOT CONNECTED - DO NOT RUN MONITORING and ALLOW CONNECTION ATTEMPTS if(monitorTimer) { // Stop trying to reconnect with the observer socket, thus allowing the Main socket to connect NSLog(@"Stopping the Monitoring Method..."); [monitorTimer invalidate]; monitorTimer = nil; }else{ NSLog(@"Connection Monitoring already halted!"); } // If the reconnectTimer is not running (it shouldnt be, otherwise something is wrong) then go ahead and run it // This will attempt to reconnect asyncSocketMain if(!reconnectTimer) { NSLog(@"Starting the reconnectTimer"); [ asyncSocketMain disconnect ]; // Deallocate the main socket to allow for reconnects [ self setupReconnectTimer ]; }else{ NSLog(@"Reconnection Attempts are already happening! [ reconnectTimer: %@ ]",reconnectTimer); } break; case 7: NSLog(@"OBSERVER ERROR - The Main Socket is Already Connected!"); break; } } else{ NSLog(@"An Unknown Socket Connection Encountenetworking and Error..."); } } // end void 

Como reference, escribo todos los datos en asyncSocketMain . El object asyncSocketObserver siempre intenta conectarse en un timer, siempre que deviceIsConnected = TRUE .

Esta puede no ser la forma más elegante de monitorear la connection, pero sí funciona. Tan pronto como desconecto la alimentación de mi dispositivo, asyncSocketObserver time de espera, que luego (como por código) detiene todas las "monitorizaciones de connection" y crea un timer de "reconnection", que luego se invalida tan pronto como se establece la connection.

Gracias de nuevo a @rokjarc por su valioso conocimiento y aportes, espero que el semi-pseudo código que he proporcionado aquí (¡que de hecho funciona según lo previsto!) Ayuda a algún otro desarrollador, ya que es algo que he tenido problemas con al less ¡una semana!

Tendrías el mismo problema con CocoaAsyncSocket (aunque es un gran proyecto). Las conexiones TCP funcionan normalmente, pero la desconnection solo se detecta si el otro lado se desconecta "según las reglas". Si una línea está en quiebra (apagando el dispositivo, saliendo del range …) necesitas algún mecanismo para detectar esto. Esta es la tarea de los clientes.

Estás en el path correcto pensando en usar NSTimer .

Hay varias forms de lidiar con esto, principalmente dependiendo de una cosa: ¿el dispositivo envía datos por sí mismo (después de una connection exitosa) o su aplicación tiene que solicitar datos?

Pero la solución es básicamente la misma. Después de hacer una connection exitosa, crea un NSTimer repetible. También necesitará algún tipo de variable dataAge . Este timer (podría llamarse connectionMonitor ) aumenta dataAge cada vez que se dispara.

Si dataAge es demasiado grande (> 5s), destruye la connection (también el timer) y comienza a conectar el procedimiento por completo.

Por supuesto, dataAge debe restablecerse cada vez que obtiene datos del dispositivo.

También debe manejar los events NSStreamEventErrorOccurnetworking y NSStreamEventEndEncountenetworking : probablemente con la destrucción de connectionMonitor y el reinicio del procedimiento de connection.

Probablemente conozca este tutorial, pero por las dudas: Progtwigción de networkinges iPhone