iOS Voip Socket не будет работать в фоновом режиме

Я получаю сокет VOIP для работы в фоновом режиме в приложении iOS.

Мое соединение работает нормально, но оно не просыпается, когда мое приложение переходит в фоновый режим. Однако, если я снова открою приложение, оно будет отвечать на любые сообщения, полученные во время сна.

Я настроил свой поток следующим образом:

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);

Затем мой обратный вызов настроен следующим образом:

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» вызывается, когда я получаю данные и приложение открыто, но этого не происходит, если приложение свернуто. Однако, когда приложение возвращается, оно обрабатывает все данные, которые оно получило в свернутом виде.

Я добавил тег voip в info.plist. Мой CFReadStreamSetProperty возвращает значение true. Я работаю на устройстве, а не на симуляторе. Это все еще не работает, поэтому я не знаю, в чем может быть моя проблема. Я, наверное, только что сделал что-то глупое, но в сети почти нет ничего, с чем можно было бы проверить мой код.

РЕДАКТИРОВАТЬ: я не могу проверить ни один из ответов, потому что я больше не работаю над этим проектом и не имею доступа к mac/iOs sdk. Если кто-то с похожей проблемой нашел один из приведенных ниже ответов полезным, дайте мне знать, и я проголосую за лучший ответ.


person Joel    schedule 06.07.2011    source источник
comment
+1: вы также добавили аудиотег в plist? Я просто читаю руководство по программированию приложений о многозадачности, но это кажется чем-то неправильным. Надеюсь, вы получите ответ. хороший вопрос.   -  person Jesse Naugher    schedule 06.07.2011
comment
Я не воспроизводю аудио (пока), так что это не должно иметь значения. Но я все равно пытался, и это не сработало. Спасибо, что хотя бы дали мне что-то попробовать, я просто часами ломал голову.   -  person Joel    schedule 06.07.2011
comment
Можете ли вы опубликовать код в обработчике тайм-аута Keep Alive?   -  person Jesse Naugher    schedule 06.07.2011
comment
@Joel Привет, Джоэл, если вы можете решить проблему, обновите здесь.   -  person User97693321    schedule 31.10.2012
comment
Опять же, я больше не работаю над этим проектом и больше не имею доступа к коду. Извини :(   -  person Joel    schedule 03.11.2012


Ответы (5)


Если вы хотите, чтобы ваше приложение VOIP работало в фоновом режиме, за исключением этих базовых настроек в файле plist, вам нужен сокет TCP, для которого установлено свойство VOIP, тогда система iOS позаботится об этом сокете для вас, когда ваше приложение войдет в фоновый режим, все было «спящим», кроме этого сокета tcp. и если сервер VOIP отправит некоторые данные, думая, что это TCP-сокет, ваше приложение будет пробуждено в течение 10 секунд. в это время вы можете опубликовать локальное уведомление.

Только сокет Tcp может быть установлен как сокет VOIP. Но насколько я знаю, в основном приложения VOIP основаны на сокете UDP. если вы не хотите отделять сокет управления от сокета данных. вам следует создать еще один сокет tcp, который будет фокусироваться на «бодрствующем» вашем приложении, и, исходя из моего личного опыта, очень сложно поддерживать синхронизацию этого «бодрствующего» сигнала и реального управляющего сигнала sip, приложение всегда пропускает запрос на приглашение sip.

Таким образом, лучший способ — отделить одно управление sip от сокета данных UDP, сделать его сокетом tcp, это лучшее решение, но никогда не использовать сокет tcp для передачи голосовых данных.

Еще один грязный способ: постоянно держать приложение в активном состоянии. Как я уже сказал, каждый отдельный TCP, полученный приложением, думал, что tcp-сокет VOIP будет держать приложение в активном состоянии в течение 10 секунд, поэтому в конце этого периода (через 9 секунд) вы можете отправить ответ на сервер, чтобы запросить другой сигнал, когда поступит следующий сигнал, приложение снова активируется, через 9 секунд снова отправит ответ. продолжайте делать это, ваше приложение проснется навсегда.

person Joe Qian    schedule 12.09.2011
comment
@Joel: Вы можете получать голосовые данные, пока приложение работает в фоновом режиме? Если этот пост полезен, обновите его здесь и примите любой пост. - person User97693321; 31.10.2012
comment
Я не уверен, что вы имеете в виду «в фоновом режиме», если приложение полностью находится в фоновом режиме (не в режиме вызова), могут быть получены только данные TCP. - person Joe Qian; 26.12.2012
comment
@JoeQian Пожалуйста, не могли бы вы предоставить более подробную информацию и фрагмент кода, потому что я совершенно новичок в этом. - person S.J; 22.05.2013

Я застрял в точно таком же сценарии.
Моя проблема заключалась в том, что я настроил более одного сокета в качестве сокета Voip.

вы можете ознакомиться с документацией Apple о voip что они говорят:
"Настройте один из сокетов приложения для использования VoIP"

Я предполагаю, что они пробуждают ваше приложение только в соответствии с одним сокетом.

все остальные упомянутые вещи по-прежнему верны:

  • kCFStreamNetworkServiceTypeVoIP
  • 'info.plist' UIBackgroundModes: voip, аудио
  • Ключ UIRequiresPersistentWifi 'info.plist'
  • НЕ будет работать над симулятором
person avishic    schedule 14.08.2011

Я тоже сталкиваюсь с той же проблемой. но в моем случае все работает нормально, если только не происходят изменения сети. Я использовал класс «доступности» Apple для обнаружения изменений в сети. если приложение находится в фоновом режиме, до какого-то момента мой сокет работает, даже если я вручную переключил свою сеть на следующее.

  1. Wi-Fi -> 3g
  2. 3G -> Wi-Fi

Через некоторое время, скажем, снова я пытаюсь переключиться на сеть вручную. ничего не происходит, кажется, мое приложение не обнаруживает изменения в сети. Я прочитал приведенный ниже документ Apple. я уверен, что делаю неправильно (или) не понял шаги 3 и 6.

Существует несколько требований для реализации приложения VoIP:

<сильный>1. Добавьте ключ UIBackgroundModes в файл Info.plist вашего приложения. Установите значение этого ключа в массив, который включает строку voip.

  1. Настройте один из сокетов приложения для использования VoIP.

  2. Прежде чем перейти в фоновый режим, вызовите метод setKeepAliveTimeout:handler: для установки обработчика, который будет выполняться периодически. Ваше приложение может использовать этот обработчик для поддержания соединения со службой.

  3. Настройте аудиосессию для обработки переходов к активному использованию и обратно.

<сильный>5. Чтобы обеспечить лучшее взаимодействие с пользователем на iPhone, используйте платформу Core Telephony, чтобы настроить свое поведение в отношении телефонных звонков по сотовым телефонам; см. Справочник по базовой платформе телефонии.

  1. Чтобы обеспечить хорошую производительность вашего приложения VoIP, используйте структуру конфигурации системы, чтобы обнаруживать изменения в сети и позволять вашему приложению спать как можно дольше.
person user1586900    schedule 30.08.2012
comment
Я сталкиваюсь с той же проблемой. У вас есть какая-либо причина, по которой вы не получаете обратный вызов при изменении сети? Я думаю, что когда приложение переходит в фоновый режим, ios переводит приложение в состояние «Приостановлено», поэтому приложение не может обнаружить изменение сети. - person Khushbu Shah; 11.04.2013

Возможно, вам потребуется установить <key>UIBackgroundModes</key><array><string>audio</string></array> в Info.plist, и вам нужно убедиться, что аудиосессия активна/работает/что-то еще, прежде чем переключать приложения (предполагается, что вы не начнете внезапно записывать/воспроизводить музыку/что-то еще, когда ваш приложение находится в фоновом режиме).

В документах говорится, что "аудио " позволяет воспроизводить звук в фоновом режиме, но предположительно это относится и к записи звука. Если это не сработает, есть несколько вещей, которые вы можете попробовать:

  • Установите оба "voip" и "аудио".
  • Воспроизвести тишину (это может быть проще всего сделать с помощью Audio Queue API).
person Sneakyness    schedule 28.07.2011
comment
Воспроизведение тишины — это уловка, обнаруженная Apple, которая не позволяет опубликовать приложение в магазине (если только приложение не предоставляет функцию реального звука) - person Albrecht Andrzejewski; 23.04.2012
comment
Сокет VIOP был бы настоящей аудиосистемой, не так ли? У вас есть доказательства этого отказа? Я никогда ничего этого не видел. - person Sneakyness; 27.04.2012
comment
Из первого ответа в этой ветке the-backgro" title="как приложение ios может поддерживать соединение tcp на неопределенный срок, в то время как в backgro">stackoverflow.com/questions/5840365/ есть история о приложении, отклоненном Apple за воспроизведение беззвучного звука : https://web.archive.org/web/20140806111426/http://tapbots.com/blog/pastebot/pastebot-music-in-background - person RenniePet; 08.03.2017

У вас есть 'applicationDidEnterBackground:' в вашем делегате приложения. Я почти уверен, что где-то читал (не могу найти), что вам нужно определить его, чтобы ios распознал, что вы поддерживаете фоновые режимы. Вам не нужно ничего реализовывать в нем.

e.g.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
person Shane Powell    schedule 15.08.2011
comment
Это просто метод из протокола UIApplicationDelegate. Это необязательно и не связано с этим вопросом. - person gurooj; 04.12.2015