iOS Voip Socket无法在后台运行

21

我正在iOS应用程序中使VOIP套接字在后台运行。

我的连接工作正常,但当我的应用程序进入后台时,它无法唤醒。但是,如果我重新打开应用程序,它会响应任何在休眠期间收到的消息。

我像这样设置了我的流:

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的权限。如果有人发现以下任何答案有用,请告诉我,我将投票支持它。

+1:你是否在plist中添加了“audio”标签?我正在阅读有关多任务处理的应用程序编程指南,但这似乎是一些不规则的事情。希望你能得到答案。好问题。 - Jesse Naugher
我还没有播放任何音频,所以这不应该有影响。但是我还是尝试了一下,但没有成功。感谢你至少给了我一些尝试的东西,我已经苦思冥想了几个小时了。 - Joel
你能把保持连接超时处理程序的代码发布出来吗? - Jesse Naugher
@Joel 你好 Joel,如果你能够解决这个问题,请在这里更新。 - User97693321
我已经不再参与这个项目的工作,也无法访问代码了。很抱歉 :( - Joel
5个回答

29

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

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

所以,最好的方法是将sip控制单独从UDP数据套接字中分离出来,将其作为tcp套接字,这是最佳解决方案,但永远不要使用tcp套接字传输语音数据。

另一种粗暴的方法:始终使应用程序处于唤醒状态。 正如我所说,每个TCP信号通过“VOIP” tcp套接字接收到的应用程序将保持唤醒10秒钟,因此在此持续时间结束时(在9秒后),您可以向服务器发送响应以请求另一个信号,当下一个信号到达时,应用程序将再次唤醒,在9秒后再次发送响应。一直这样做,您的应用程序将永远保持唤醒。


@Joel:当应用程序在后台运行时,您能否接收语音数据?如果此帖子有帮助,请在此更新并接受任何帖子。 - User97693321
我不确定你所说的“在后台”是什么意思,如果应用程序完全在后台(不在通话中),那么只能接收TCP数据。 - Joe Qian
@JoeQian 请您能否提供更详细的说明和一些代码片段,因为我对此完全是新手。 - S.J

5

我曾经遇到过完全相同的情况。

我的问题是,我已经将多个套接字配置为Voip套接字

你可以在苹果关于Voip的文档中看到他们说:


"将应用程序的一个套接字配置为VoIP使用"

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

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

  • kCFStreamNetworkServiceTypeVoIP
  • 'info.plist' UIBackgroundModes: voip, audio
  • 'info.plist' UIRequiresPersistentWifi key
  • 无法在模拟器上工作

3
我也遇到了同样的问题。但在我的情况下,一切都是正常的,除非网络发生变化。我使用苹果的“reachability”类来检测网络变化。如果应用程序在后台一段时间内,即使我手动切换网络,套接字也会工作。

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

过了一段时间,再次手动尝试网络切换。好像没有任何事情发生,似乎我的应用程序没有检测到网络变化。我阅读了下面的苹果文档。我确定做错了(或者)误解了第3步和第6步。

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

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

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

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

  3. 配置音频会话以处理与活动使用的过渡。

5. 为确保iPhone上更好的用户体验,请使用Core Telephony框架来调整您与基于蜂窝的电话呼叫相关的行为;请参阅Core Telephony Framework参考。

  1. 为了确保VoIP应用程序的良好性能,请使用System Configuration框架来检测网络变化,并允许您的应用程序尽可能休眠。

我也遇到了同样的问题。你有没有找到网络变化时没有回调的原因?我认为当应用程序进入后台时,iOS会将其置于挂起状态,这就是应用程序无法检测到网络变化的原因。 - Khushbu Shah

0

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

文档说明“音频”可以让您在后台播放音频,但是可能也适用于录制音频。如果不起作用,有几个方法可以尝试:

  • 同时设置“voip”和“音频”。
  • 播放静音(使用音频队列API可能是最简单的方法)。

播放静音是一种被苹果检测到的技巧,除非应用程序提供真正的音频功能,否则将阻止其在商店上发布。 - Albrecht Andrzejewski
一个VIOP套接字将是一个真正的音频功能,不是吗?你有任何拒绝的证据吗?我从未见过这方面的任何东西。 - Sneakyness
从这个线程的第一个答案https://dev59.com/G2025IYBdhLWcg3wtobX中,有一个应用程序因播放静音音频而被苹果拒绝的故事:https://web.archive.org/web/20140806111426/http://tapbots.com/blog/pastebot/pastebot-music-in-background - RenniePet

-1
你的应用程序委托中是否有'applicationDidEnterBackground:'方法?我相信我曾经在某个地方(但现在找不到了)读到过,你需要定义它,以便iOS识别你支持后台模式。你不需要在其中实现任何内容。
例如:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}

1
这只是UIApplicationDelegate协议中的一个方法。它是可选的,与此问题无关。 - gurooj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接