2011-05-13 23 views
20

Mi aplicación Requisito: Debo mantener una conexión de socket para activar la notificación local en el servidor push sin utilizar notificaciones push (APN) por alguna razón. Así que estoy usando la capacidad de fondo VOIP de iPhone para mantener la conexión del socket.¿Cómo mantener la conexión de socket VOIP en segundo plano?

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 para que mi aplicación escuche el socket todo el tiempo?

configuración del flujo de clientes 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 corriente en el controlador applicationDidEnterBackground:

[[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. Di mi reinicia el servidor 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?

+1

El código que ha incluido aquí, ¿es código de trabajo? –

Respuesta

19

También es necesario asegurarse de que ha configurado el archivo en su PLIST

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

El zócalo será administrado por el IOS, mientras que su aplicación es en el fondo. Su aplicación recibirá el tiempo de CPU tan pronto como haya datos disponibles en el socket. Así, en el Runloop estoy comprobando ht

En mi caso el protocolo de señalización está trabajando en un hilo separado, por lo que estoy haciendo girar la Runloop mi auto

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

y parando cuando sea necesario:

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

Para los enchufes en Runloop he fijado las funciones de controlador antes de programar ellas 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 llamará cuando su voluntad conector tiene algo de información para usted e incluso si su aplicación 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 consciente de que el cliente todavía está vivo enviamos el comando ping para servidor cada 10 minutos para que el controlador KeepAlive esté configurado en 600. Puede usar otros valores para guardar la batería, pero empeorará la detección de las desconexiones en el lado del cliente y 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); 
} 

Dónde SchedulePing se ejecutará (0) de la siguiente manera:

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

Y StartLongBGTask es una

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

Esto es necesario para asegurarse de que la aplicación no va a ser suspendida 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 de fondo correctamente cuando ya no las necesite. El sistema eliminará otra aplicación sabia cuando se exceda el tiempo de espera de bg.

+0

Acabo de tropezar con una publicación que decía que se rechazó una aplicación porque no proporcionaba ningún servicio de Voz sobre IP pero usaba el modo de fondo VoIP. Alguna idea sobre esto? Entonces, si estoy desarrollando una aplicación de mensajería instantánea y quiero que el socket esté vivo incluso en segundo plano, ¿puedo seguir utilizando este enfoque anterior, a pesar de que no apoyaré ninguna función de voz? – Roshit

+0

¿Obtuvo su aplicación aprobada?- Estoy en una situación similar, pero afortunadamente puedo usar la distribución empresarial de todos modos – gheese

+0

También me interesa esto. Por favor, entra si tienes algunas historias! – Nailer

2

Apple ha proporcionado detalles sobre esto en documentation.You oficial pueden encontrar aquí https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html

De acuerdo con la documentación

Hay 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 gestionar 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 el buen rendimiento de 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.

Al incluir el valor de VoIP en la clave UIBackgroundModes, el sistema sabe 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.

Cuestiones relacionadas