配置iOS VoIP应用在睡眠/后台模式下运行。

7
我正在开发一个基于VoIP的iOS(7.1)应用程序。它的底层套接字编程是用C++编写的,而不是Objective C。该应用在前台运行良好,但在休眠/后台模式下无法从服务器接收任何通信。
根据苹果文档,我们必须为其中一个应用程序套接字配置VoIP使用。我无法弄清楚如何配置C++套接字(因为有许多套接字;即SSL、SIP、RESTful)。
我的意图是将应用程序在睡眠模式下运行,直到被杀死。尝试了一些链接,甚至从SO中找到了一些链接,但由于我是新手,所以我想要一个逐步的配置过程。 [注:在某个地方我找到了CoreFoudation框架,我需要使用它吗?]

请发布您的崩溃日志中的符号化堆栈跟踪和错误信息。 - Aaron Brager
@AaronBrager- 感谢您的回复。我已经使用了[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ }]; 这个处理程序是用于在后台运行长时间任务的。我知道从iOS 7开始,iOS允许最多3分钟。我们应该在处理程序内部调用[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];。我之所以崩溃是因为我没有调用endBackgroundTask函数。我正在尝试允许后台任务。这就是为什么我会收到崩溃的原因。我们的主要目的是为Voip使用配置套接字。 - Vinay Podili
2个回答

14

编辑:从iOS8开始,苹果公司推出了PushKit框架来释放我们需要进行配置的VoIP应用程序,并且它还可以减少能源使用。你真的需要迁移到它。

以下是开发VoIP应用程序的提示,参考自Apple官方文档

网络电话(VoIP)应用程序允许用户通过互联网连接而不是设备的蜂窝服务打电话。此类应用程序需要维护与其关联服务的持久网络连接,以便接收来电和其他相关数据。系统允许将VoIP应用程序挂起而不是一直保持唤醒状态,并为它们提供监视套接字的工具。当检测到传入流量时,系统会唤醒VoIP应用程序并将其套接字控制权交还给它。

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

  1. 为应用程序启用Voice over IP后台模式。(由于VoIP应用程序涉及音频内容,建议您还启用音频和AirPlay后台模式。)您可以在Xcode项目的功能选项卡中启用后台模式。
  2. 为VoIP使用配置应用程序的一个套接字。
  3. 在进入后台之前,调用setKeepAliveTimeout:handler:方法来安装定期执行的处理程序。您的应用程序可以使用此处理程序来维护其服务连接。
  4. 配置您的音频会话以处理从活动使用到非活动使用的转换。
  5. 为了在iPhone上保证更好的用户体验,请使用Core Telephony框架来调整与基于蜂窝电话的通话相关的行为;请参阅Core Telephony Framework参考文档。
  6. 为确保您的VoIP应用程序具有良好的性能,请使用System Configuration框架检测网络变化并尽可能让您的应用程序休眠。

启用VoIP后台模式使系统知道它应该根据需要允许应用程序在后台运行以管理其网络套接字。此关键字还允许您的应用程序播放背景音频(虽然仍然鼓励启用音频和AirPlay模式)。支持此模式的应用程序也会在系统引导后立即在后台重新启动,以确保VoIP服务始终可用。


以下代码显示了如何为应用程序的套接字配置VoIP使用。

步骤1:连接到服务器

uint16_t port ;
NSString *strIp ;
char ip[20] = {0} ;
memset(ip, 0, sizeof(ip)) ;
memcpy(ip, [strIp UTF8String], [strIp length]) ;

clientSocket = socket(AF_INET, SOCK_STREAM, 0) ;
struct sockaddr_in server_addr ;
bzero(&server_addr, sizeof(server_addr)) ;
server_addr.sin_port = htons(port) ;
server_addr.sin_addr.s_addr = inet_addr(ip) ;
server_addr.sin_family = AF_INET ;

int i = connect(clientSocket, (const struct sockaddr *)&server_addr, sizeof(server_addr)) ;
if (i >= 0) {
}

服务器端代码可能在 C++ 环境中,但你可以将 clientSocket 传递给 Objective-C 实例,它是一个 int 值。

步骤2:创建和配置读写流

连接到服务器后,需要使用 CFStreamCreatePairWithSocket() 基于 clientSocket 创建读写流,并使用 NSStreamNetworkServiceTypeVoIP 设置流的属性。

定义读写流并保持强引用。当连接丢失时,关闭并释放它们。

@property (nonatomic, strong) NSInputStream *inputStream ;
@property (nonatomic, strong) NSOutputStream *outputStream ;

然后配置流:

CFReadStreamRef readStreamRef = nil ;
CFWriteStreamRef writeStreamRef = nil ;
CFStreamCreatePairWithSocket(NULL, clientSocket, &readStreamRef, &writeStreamRef) ; // the socket must have already been connected.
_inputStream = (__bridge_transfer NSInputStream *)readStreamRef ;
_outputStream = (__bridge_transfer NSOutputStream *)writeStreamRef ;
[_inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[_outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[_inputStream open] ;
[_outputStream open] ;

在使用读写流连接之前,请确保套接字已经连接。

第三步:保持连接

[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
    // the code to check if the socket is connected to server
    // if not, reconnect to server 
    // and re-set the read stream and write stream in step2
}] ;
当您的应用程序进入后台时,套接字由系统管理,当服务器向您的应用程序发送数据包时,系统会唤醒它并将数据包传递给它。您只有几秒钟来处理数据,因此不要在这里做太多的工作。由于这是一个VoIP应用程序,所以应该使用套接字来通知用户有来电,并可以推送本地通知以让用户知道。
因为VoIP应用程序需要保持运行状态才能接收呼入电话,所以如果应用程序以非零退出代码退出,系统会自动重新启动应用程序。(当存在内存压力并且您的应用程序因此而终止时可能会发生这种类型的退出。)但是,终止应用程序也会释放其所有套接字,包括用于维护VoIP服务连接的套接字。因此,当应用程序启动时,它总是需要从头开始创建其套接字。
我已经在这里创建了一个示例项目,相关的服务器端代码在这里

2
@VinayPodili 在connect成功后,您应该传递套接字并使用CFStreamCreatePairWithSocket函数创建读写流,然后配置这些流。 - KudoCC
1
@KudoCC 你好,我已经将你的代码部分成功地集成到我的项目中。唯一的问题是,在3-10分钟后,从服务器发送的数据需要长达2分钟才能到达客户端。你有这方面的经验或者解释吗?谢谢。 - Kevin
1
@Kevin,不,我没有这方面的经验。明天早晨我可以在我的电脑上尝试一下。 - KudoCC
1
@Kevin 我已经测试过了,在这里运行得很好,我不知道那边出了什么问题。你可以使用我的客户端服务器项目进行测试,如果出现了同样的问题,请给我留言。 :) - KudoCC
谢谢大家,你们的回答和评论帮了我很多 :) - Vinay Podili
显示剩余6条评论

2

除了配置套接字外,您还需要对iOS应用程序的Info.plist文件进行一些更改,在其中指定voip后台模式。

更多详细信息可以在此处找到:Apple Dev文档


谢谢您的回复。是的,我已经在info.plist中启用了voip后台模式。我猜需要配置socket。我需要使用CFStreamCreatePairWithSocket来配置socket吗? - Vinay Podili
我认为你不需要任何特定的类来进行配置,只需按照苹果文档建议使用setTimeoutInterval:handler:即可。据我所知,你的套接字将会定期关闭,因此你需要重新连接。 - Roman
谢谢。那我不需要设置kCFStreamNetworkServiceTypeVoIP吗?而且我有多个在线程上运行的套接字。在iOS中,每当我们进入后台,应用程序将被挂起。因此,套接字连接将会丢失。我想在睡眠模式下保持长时间的套接字连接。 - Vinay Podili
嗨Vinay,为了保持套接字连接的活性,您需要使用此方法:NSApplication setKeepAliveTimeout:handler:。这应该使您能够控制后台中套接字保持打开的时间。 - Roman
2
系统不会一直保持VoIP应用程序的开启状态,而是允许它们被暂停,并提供监视它们套接字的功能。当检测到传入流量时,系统将唤醒VoIP应用程序并将其套接字的控制权返回给它。因此,必须“为VoIP使用配置应用程序的其中一个套接字”,否则无法接收传入呼叫。例如,NSApplication setKeepAliveTimeout:handler:用于维护其服务连接:向服务器发送保持活动数据包。 - KudoCC

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