编辑:从 iOS8 开始,苹果引入了PushKit框架来释放我们需要做的配置 VoIP 应用程序的工作,它也减少了能源使用。你真的需要迁移到它
有一些开发 VoIP App 的技巧,来自Apple的参考
Internet 协议语音 (VoIP) 应用程序允许用户使用 Internet 连接而不是设备的蜂窝服务拨打电话。这样的应用程序需要保持与其关联服务的持久网络连接,以便它可以接收来电和其他相关数据。该系统不是让 VoIP 应用程序一直处于唤醒状态,而是允许它们被暂停并为它们提供监控其套接字的设施。当检测到传入流量时,系统会唤醒 VoIP 应用程序并将其套接字的控制权返回给它。
实施 VoIP 应用程序有几个要求:
- 为您的应用启用 IP 语音后台模式。(因为 VoIP 应用程序涉及音频内容,建议您同时启用 Audio 和 AirPlay 后台模式。)您在 Xcode 项目的 Capabilities 选项卡中启用后台模式。
- 为 VoIP 使用配置应用程序的其中一个套接字。
- 在移动到后台之前,调用 setKeepAliveTimeout:handler: 方法来安装一个要定期执行的处理程序。您的应用可以使用此处理程序来维护其服务连接。
- 配置您的音频会话以处理与活动使用之间的转换。
- 为确保在 iPhone 上获得更好的用户体验,请使用 Core Telephony 框架来调整您与手机通话相关的行为;请参阅核心电话框架参考。
- 为确保您的 VoIP 应用程序具有良好的性能,请使用系统配置框架来检测网络变化并让您的应用程序尽可能地休眠。
启用 VoIP 后台模式让系统知道它应该允许应用程序根据需要在后台运行以管理其网络套接字。此键还允许您的应用播放背景音频(尽管仍鼓励启用音频和 AirPlay 模式)。支持此模式的应用程序也会在系统启动后立即在后台重新启动,以确保 VoIP 服务始终可用。
下面的代码显示了如何配置应用程序的套接字以使用 VoIP。
第一步:连接服务器
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 步:创建和配置读写流
连接到服务器后,您需要根据clientSocket
using创建一个读写流,CFStreamCreatePairWithSocket()
并使用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 服务连接的套接字。因此,当应用程序启动时,它总是需要从头开始创建它的套接字。
我在这里创建了一个示例项目,相关的服务器端代码在这里。