web-dev-qa-db-fra.com

iOS Voip Socket ne fonctionnera pas en arrière-plan

Je reçois une prise VOIP à exécuter en arrière-plan dans une application iOS.

Ma connexion fonctionne correctement, mais elle ne se réveille pas lorsque mon application passe en arrière-plan. Si je rouvre l’application, elle répond à tous les messages qu’elle a reçus pendant son sommeil.

Je crée mon flux comme ceci:

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
                                   (CFStringRef) @"test.iusealocaltestserver.com",
                                   5060,
                                   &myReadStream,
                                   &myWriteStream);
CFReadStreamSetProperty (    myReadStream,
                             kCFStreamNetworkServiceType,
                             kCFStreamNetworkServiceTypeVoIP
                             );

CFSocketNativeHandle native;
CFDataRef nativeProp = CFReadStreamCopyProperty(myReadStream, kCFStreamPropertySocketNativeHandle);

CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
CFRelease(nativeProp);

CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);

CFSocketGetContext(theSocket,&theContext);    


CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable | 
kCFStreamEventErrorOccurred     |
kCFStreamEventEndEncountered    |
kCFStreamEventOpenCompleted;

CFReadStreamSetClient(myReadStream,
                           readStreamEvents,
                           (CFReadStreamClientCallBack)&MyCFReadStreamCallback,
                      (CFStreamClientContext *)(&theContext));

CFReadStreamScheduleWithRunLoop(myReadStream, CFRunLoopGetCurrent(),
                                kCFRunLoopCommonModes);

Ensuite, mon rappel est configuré comme suit:

static void MyCFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo);

static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"Callback Happened");

   [pool release];
}

"Callback Happened" est appelé lorsque je reçois des données et que l'application est ouverte, mais ce n'est pas le cas si l'application est réduite. Lorsque l'application est réactivée, elle traite toutes les données qu'elle a reçues tout en les réduisant. 

J'ai ajouté le tag voip à l'info.plist. Mon CFReadStreamSetProperty renvoie true. Je cours sur un appareil pas un simulateur. Cela ne fonctionne toujours pas, alors je ne sais pas ce que mon problème pourrait être. J'ai probablement juste fait quelque chose de stupide, mais il n'y a presque rien en ligne pour vérifier mon code.

EDIT: Je ne peux tester aucune des réponses car je ne travaille plus sur ce projet et je n’ai pas accès à un sdk mac/iOs. Si une personne ayant un problème similaire trouve utile l’une des réponses ci-dessous, faites-le-moi savoir et je voterai pour votre meilleure réponse.

21
Joel

Si vous souhaitez laisser votre application VOIP s'exécuter en arrière-plan, à l'exception des paramètres de base du fichier plist, vous avez besoin d'un socket TCP dont la propriété est définie sur VOIP. Le système iOS se chargera de ce socket pour vous. lorsque votre application entre en arrière-plan, tout était en veille, à l'exception de la prise TCP. et si le serveur VOIP envoie des données en pensant que TCP socket, votre application sera réveillée pendant 10 secondes. pendant ce temps, vous pouvez poster une notification locale.

Seule la prise TCP peut être définie en tant que prise VOIP. Mais à partir de ce que je sais, les applications VOIP sont principalement basées sur le socket UDP. si vous ne voulez pas séparer le socket de contrôle du socket de données. vous devriez créer un autre socket TCP centré sur votre application, et d'après mon expérience personnelle, il est très difficile de garder ce signal "éveillé" et le signal de commande sip réel synchronisés; l'application rate toujours la demande d'invitation sip. 

Donc, le meilleur moyen est de séparer le sip control single du socket de données UDP, faites-le en tant que socket TCP, c'est la meilleure solution, mais n'utilisez jamais de socket TCP pour transférer des données vocales. 

Une autre façon de faire: garder l’application constamment éveillée. Comme je l’ai dit, chaque TCP application reçue pensait que la prise 'VOIP' tcp maintiendrait l’application éveillée pendant 10 secondes, donc à la fin de cette durée (après 9 secondes), vous pouvez envoyer une réponse au serveur pour demander un autre signal. Lorsque le prochain signal est arrivé, l'application sera de nouveau réveillée. Après 9 secondes, elle enverra une réponse. Continuez comme cela, votre application restera éveillée pour toujours.

28
Joe Qian

J'étais coincé avec exactement le même scénario.
Mon problème était que j'ai configuré plus d'une prise en tant que prise Voip .

vous pouvez voir sur les documentations d’Apple sur la VoIP qu’elles disent:
"Configurez one des sockets de l’application pour l’utilisation VoIP"

Je suppose qu'ils ne font que réveiller votre application en fonction d'une seule prise.

toutes les autres choses mentionnées sont toujours correctes:

  • kCFStreamNetworkServiceTypeVoIP 
  • 'info.plist' UIBackgroundModes: voip, audio
  • Clé 'info.plist' UIRequiresPersistentWifi
  • ne fonctionnera pas sur simulateur
5
avishic

Je suis également confronté au même problème. mais dans mon cas, tout fonctionne bien, à moins que des changements de réseau ne se produisent. J'ai utilisé la classe "d'atteignabilité" Apple pour détecter les modifications du réseau. si l'application est en arrière-plan jusqu'à un moment donné, ma prise fonctionne même si j'ai manuellement basculé mon réseau pour les éléments suivants.

  1. wifi -> 3g
  2. 3g -> wifi

Après quelque temps, disons encore une fois que j'essaie de changer de réseau manuellement. rien ne se passe, il semble que mon application ne détecte pas les modifications du réseau. J'ai lu le document Apple ci-dessous. Je suis sûr de ne pas avoir compris (ou mal compris) les étapes 3 et 6.

Il existe plusieurs exigences pour la mise en œuvre d'une application VoIP:

1. Ajoutez la clé UIBackgroundModes au fichier Info.plist de votre application. Définissez la valeur de cette clé sur un tableau contenant la chaîne voip.

  1. Configurez l’un des sockets de l’application pour l’utilisation de la VoIP.

  2. Avant de passer en arrière-plan, appelez la méthode setKeepAliveTimeout: handler: pour installer un gestionnaire à exécuter périodiquement. Votre application peut utiliser ce gestionnaire pour maintenir sa connexion de service.

  3. Configurez votre session audio pour gérer les transitions vers et depuis une utilisation active.

5. Pour assurer une meilleure expérience utilisateur sur iPhone, utilisez le framework Core Telephony pour ajuster votre comportement en ce qui concerne les appels téléphoniques depuis des cellules. voir Référence de base du cadre téléphonique.

  1. Pour assurer de bonnes performances à votre application VoIP, utilisez le cadre de configuration du système pour détecter les modifications apportées au réseau et permettre à votre application de dormir autant que possible.
3
user1586900

Avez-vous un 'applicationDidEnterBackground:' dans le délégué de votre application. Je suis presque sûr que j'ai lu quelque part (que je ne trouve pas), que vous devez le définir pour que iOS reconnaisse que vous supportez les modes d'arrière-plan. Vous n'avez besoin de rien implémenter.

par exemple.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
0
Shane Powell

Vous devrez peut-être définir <key>UIBackgroundModes</key><array><string>audio</string></array> dans Info.plist et vous assurer que la session audio est active/en cours d’exécution/quoi que ce soit avant de changer d’application (l’hypothèse est que vous ne commencerez pas à enregistrer/écouter de la musique l'application est en arrière-plan).

Les docs disent que "audio" vous permet de jouer de l’audio en arrière-plan, mais cela vaut aussi pour l’enregistrement audio. Si cela ne fonctionne pas, vous pouvez essayer quelques solutions:

  • Définissez "voip" et "audio".
  • Restez silencieux (cela pourrait être plus facile avec l'API Audio Queue).
0
Sneakyness