21

我正在获取一个 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];
}

当我收到数据并且应用程序打开时,会调用“发生回调”,但如果应用程序被最小化,则不会。但是,当应用程序重新启动时,它会在最小化时处理它收到的任何数据。

我将 voip 标签添加到 info.plist。我的 CFReadStreamSetProperty 返回 true。我在设备而不是模拟器上运行。它仍然不起作用,所以我不知道我的问题可能是什么。我可能只是做了一些愚蠢的事情,但是网上几乎没有任何东西可以检查我的代码。

编辑:我无法测试任何答案,因为我不再从事这个项目并且无法访问 mac/iOs sdk。如果有类似问题的人发现以下答案之一有用,请告诉我,我将投票选出最佳答案。

4

5 回答 5

29

如果你想让你的VOIP应用程序在后台运行,除了plist文件中的那些基本设置,你需要一个属性设置为VOIP的TCP套接字,当你的应用程序进入后台时,iOS系统会为你处理这个套接字,除了那个 tcp 套接字,一切都在“休眠”。如果 VOIP 服务器发送一些数据认为 TCP 套接字,您的应用程序将被唤醒 10 秒。在此期间,您可以发布本地通知。

只有 Tcp 套接字可以设置为 VOIP 套接字。但据我所知,大多数 VOIP 应用程序都是基于 UDP 套接字的。如果您不想将控制套接字与数据套接字分开。您应该创建另一个专注于“唤醒”您的应用程序的 tcp 套接字,根据我的个人经验,很难保持这个“唤醒”信号和真正的 sip 控制信号同步,应用程序总是错过 sip 邀请请求。

所以,最好的办法是把 sip 控制单从 UDP 数据套接字中分离出来,做成一个 tcp 套接字,这是最好的解决方案,但千万不要使用 tcp 套接字来传输语音数据。

另一种肮脏的方式:让应用程序一直保持清醒。正如我所说,应用程序收到的每个 TCP 单都认为“VOIP”tcp 套接字将使应用程序保持唤醒 10 秒,因此在此持续时间结束时(9 秒后),您可以向服务器发送响应以请求另一个信号,当下一个信号到达时,应用程序将再次唤醒,9秒后,再次发送响应。继续这样做,您的应用程序将永远唤醒。

于 2011-09-12T19:44:19.197 回答
5

我被困在完全相同的场景中。
我的问题是我已经将多个套接字配置为 Voip 套接字

您可以在Apple 的有关 voip 的文档中看到他们说:
“为 VoIP 使用配置应用程序的套接字之一

我猜他们只是根据一个套接字唤醒你的应用程序。

提到的所有其他内容仍然正确:

  • kCFStreamNetworkServiceTypeVoIP
  • 'info.plist' UIBackgroundModes:voip、音频
  • 'info.plist' UIRequiresPersistentWifi 键
  • 不会在模拟器上工作
于 2011-08-14T13:45:04.090 回答
3

我也面临同样的问题。但就我而言,一切正常,除非发生网络变化。我使用苹果“可达性”类来检测网络变化。如果应用程序处于后台直到某个时候我的套接字正在工作,即使我手动切换了我的网络以进行以下操作。

  1. 无线网络 -> 3g
  2. 3g -> 无线网络

一段时间后,让我再说一遍,我正在手动尝试网络切换。没有什么是快乐的,似乎我的应用程序没有检测到网络变化。我阅读了下面的苹果文档。我确定做错了(或)误解了第 3 步和第 6 步。

实施 VoIP 应用程序有几个要求:

1. 将 UIBackgroundModes 键添加到您应用的 Info.plist 文件中。将此键的值设置为包含 voip 字符串的数组。

  1. 为 VoIP 使用配置应用程序的其中一个套接字。

  2. 在移动到后台之前,调用 setKeepAliveTimeout:handler: 方法来安装一个要定期执行的处理程序。您的应用可以使用此处理程序来维护其服务连接。

  3. 配置您的音频会话以处理与活动使用之间的转换。

5. 为确保在 iPhone 上获得更好的用户体验,请使用 Core Telephony 框架来调整您与手机通话相关的行为;请参阅核心电话框架参考。

  1. 为确保您的 VoIP 应用程序具有良好的性能,请使用系统配置框架来检测网络变化并让您的应用程序尽可能地休眠。
于 2012-08-30T15:16:07.093 回答
0

您可能需要<key>UIBackgroundModes</key><array><string>audio</string></array>在 Info.plist 中进行设置,并且您需要在切换应用程序之前确保音频会话处于活动状态/正在运行/无论何时(假设您不会突然开始录制/播放音乐/无论何时您的应用程序在后台)。

文档说“音频”可以让你在后台播放音频,但大概这也适用于录制音频。如果它不起作用,您可以尝试以下几种方法:

  • 设置“voip”和“audio”。
  • 播放静音(这可能是使用音频队列 API 最容易做到的)。
于 2011-07-28T21:38:36.780 回答
-1

您的应用程序委托中是否有“applicationDidEnterBackground:”。我很确定我已经在某处读过(我找不到),您需要为 ios 定义它以识别您支持后台模式。你不需要在其中实现任何东西。

例如

- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
于 2011-08-15T10:24:24.647 回答