iphone sockets background voip

iphone - Cómo mantener la conexión de socket VOIP en segundo plano?



sockets background (2)

El requisito de mi aplicación : debería mantener una conexión de socket para activar la notificación local en el servidor push sin utilizar Push Notification (APN) por alguna razón. Así que estoy usando la capacidad de fondo VOIP de iPhone para mantener la conexión del socket.

1. He configurado una secuencia para VOIP para persistir la conexión del socket para que se ejecute en segundo plano, entonces, ¿qué valor de Timeout debo establecer? ¿La conexión del socket terminará una vez que expire el tiempo de espera? ¿Cómo hago que mi aplicación escuche el socket todo el tiempo?

La configuración de la corriente del cliente es la siguiente,

NSString *urlStr = @"http://192.168.0.108"; NSURL *website = [NSURL URLWithString:urlStr]; CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream); CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); NSInputStream *inputStream = (NSInputStream *)readStream; NSOutputStream *outputStream = (NSOutputStream *)writeStream; [inputStream setDelegate:self]; [inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ; [outputStream setDelegate:self]; [outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; [outputStream open];

2. ¿Debo volver a conectar la secuencia en la aplicación handlerDidEnterBackground:

[[UIApplication sharedApplication] setKeepAliveTimeout:86400 handler:^(void) { if (inputStream) [inputStream close]; if (outputStream) [outputStream close]; urlStr = @"http://192.168.0.108"; website = [NSURL URLWithString:urlStr]; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream); CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); inputStream = (NSInputStream *)readStream; outputStream = (NSOutputStream *)writeStream; [inputStream setDelegate:self]; [inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ; [outputStream setDelegate:self]; [outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inputStream open]; [outputStream open]; }];

3. Decir que mi servidor se reinicia y la aplicación está en segundo plano, ¿cómo puedo asegurar la conexión? Si la conexión Wi-Fi en mi iPhone o si termino la aplicación del servidor, la conexión se cerrará, entonces, ¿qué medidas debo tomar para que mi aplicación funcione según las expectativas?


Apple ha proporcionado detalles sobre esto en la documentación oficial. Puede encontrarlo aquí https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html

Según la documentación

Existen varios requisitos para implementar una aplicación de VoIP:

1.Agregue la clave UIBackgroundModes al archivo Info.plist de su aplicación. Establezca el valor de esta clave en una matriz que incluya la cadena de voip.

2. Configure uno de los enchufes de la aplicación para el uso de VoIP.

3.Antes de pasar a segundo plano, llame al método setKeepAliveTimeout: handler: para instalar un controlador que se ejecutará periódicamente. Su aplicación puede usar este controlador para mantener su conexión de servicio.

4. Configure su sesión de audio para manejar las transiciones hacia y desde el uso activo.

5.Para garantizar una mejor experiencia de usuario en iPhone, use el marco de Telefonía básica para ajustar su comportamiento en relación con las llamadas telefónicas basadas en celulares; vea Referencia del Marco de Telefonía Básica.

6.Para garantizar un buen rendimiento para su aplicación VoIP, use el marco de Configuración del sistema para detectar cambios en la red y permitir que su aplicación duerma tanto como sea posible.

La inclusión del valor de VoIP en la clave UIBackgroundModes le permite al sistema saber que debe permitir que la aplicación se ejecute en segundo plano, según sea necesario, para administrar sus conexiones de red. Esta tecla también permite que su aplicación reproduzca audio de fondo (aunque aún se recomienda incluir el valor de audio para la clave UIBackgroundModes). Una aplicación con esta clave también se relanza en segundo plano inmediatamente después del inicio del sistema para garantizar que los servicios de VoIP siempre estén disponibles. Para obtener más información acerca de la clave UIBackgroundModes, consulte la Referencia clave de la lista de propiedades de información.


También debe asegurarse de haber configurado el archivo pList en su

<key>UIBackgroundModes</key> <array> <string>voip</string> </array>

El socket será administrado por iOS mientras tu aplicación esté en segundo plano. Su aplicación recibirá el tiempo de CPU tan pronto como haya datos disponibles en el socket. Entonces en el runLoop estoy verificando ht

En mi caso, el protocolo de señalización funciona en un hilo separado, así que estoy girando el runLoop yo mismo

// Start runloop while (!m_needStop) { CFRunLoopRun(); }

Y deteniéndolo cuando sea necesario:

m_needStop = true; { QAutoLock l(m_runLoopGuard); if ( m_runLoop != NULL ) CFRunLoopStop(m_runLoop); }

Para los sockets en runLoop he configurado las funciones del manejador antes de programarlas en el runLoop:

int nFlags = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; CFStreamClientContext context; context.info = this; context.version = 0; context.release = NULL; context.retain = NULL; context.copyDescription = NULL; if ( !CFReadStreamSetClient(m_readStream, nFlags, NotificationProtocolHandler::ReadStreamCallback, &context) ) { ReleaseStreams(); return false; } if ( !CFWriteStreamSetClient(m_writeStream, nFlags, NotificationProtocolHandler::WriteStreamCallback, &context) ) { ReleaseStreams(); return false; }

Estas son las funciones que se invocarán cuando su socket tenga alguna información para usted e incluso si su aplicación está en segundo plano:

void NotificationProtocolHandler::ReadStreamCallback(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo) { NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo; switch (eventType) { case kCFStreamEventOpenCompleted: break; case kCFStreamEventHasBytesAvailable: handler->ProcessInput(); break; case kCFStreamEventErrorOccurred: handler->ProcessConnectionError(); break; case kCFStreamEventEndEncountered: handler->ProcessConnectionError(); break; default: break; // do nothing } } void NotificationProtocolHandler::WriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo) { NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo; switch (eventType) { case kCFStreamEventOpenCompleted: handler->ProcessOutputConnect(); break; case kCFStreamEventCanAcceptBytes: handler->ProcessReadyToWrite(); break; case kCFStreamEventErrorOccurred: handler->ProcessConnectionError(); break; case kCFStreamEventEndEncountered: handler->ProcessConnectionError(); break; default: break; // do nothing } }

Para hacer que el servidor sepa que el cliente aún está activo, enviamos el comando ping al servidor cada 10 minutos para que el controlador KeepAlive se establezca en 600. Puede usar otros valores para guardar la batería, pero empeorará la detección de las desconexiones en el cliente y el lado del servidor. Y aumentará el tiempo entre la desconexión y la reconexión.

BOOL scheduled = [app setKeepAliveTimeout:pingTimeout handler:^{ // Schedule processing after some time interval SchedulePing(0); }

Donde SchedulePing (0) se ejecutará de la siguiente manera:

StartLongBGTask(); if ( avoidFinishBgTask != NULL ) *avoidFinishBgTask = true; m_pingTimer = CreateTimer(pingTimeout, PingTimerCallback); // result is ignored

Y StartLongBGTask es un

m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{ [[UIApplication sharedApplication] endBackgroundTask:m_bgTask]; m_bgTask = UIBackgroundTaskInvalid; }];

Esto es necesario para asegurarse de que la aplicación no se suspenderá antes de enviar el ping y esperar la respuesta al ping desde el servidor. Además, si el socket ya está desconectado, podría ser necesario reconectarlo, lo que llevará algún tiempo y necesita que el proceso se ejecute en segundo plano.

Pero asegúrese de liberar las tareas en segundo plano correctamente cuando ya no las necesite. El sistema eliminará otra aplicación sabia cuando se exceda el tiempo de espera de bg.