Edit: begin from iOS8, apple introduces PushKit framework to release the work we need to do to config VoIP application and it also reduces energy usage. You really need to migrate to it
There are tips for developing a VoIP App, reference from Apple
A Voice over Internet Protocol (VoIP) app allows the user to make phone calls using an Internet connection instead of the device’s cellular service. Such an app needs to maintain a persistent network connection to its associated service so that it can receive incoming calls and other relevant data. Rather than keep VoIP apps awake all the time, the system allows them to be suspended and provides facilities for monitoring their sockets for them. When incoming traffic is detected, the system wakes up the VoIP app and returns control of its sockets to it.
There are several requirements for implementing a VoIP app:
- Enable the Voice over IP background mode for your app. (Because VoIP apps involve audio content, it is recommended that you also enable the Audio and AirPlay background mode.) You enable background modes in the Capabilities tab of your Xcode project.
- Configure one of the app’s sockets for VoIP usage.
- Before moving to the background, call the setKeepAliveTimeout:handler: method to install a handler to be executed periodically. Your app can use this handler to maintain its service connection.
- Configure your audio session to handle transitions to and from
active use.
- To ensure a better user experience on iPhone, use the Core Telephony
framework to adjust your behavior in relation to cell-based phone
calls; see Core Telephony Framework Reference.
- To ensure good performance for your VoIP app, use the System
Configuration framework to detect network changes and allow your app
to sleep as much as possible.
Enabling the VoIP background mode lets the system know that it should allow the app to run in the background as needed to manage its network sockets. This key also permits your app to play background audio (although enabling the Audio and AirPlay mode is still encouraged). An app that supports this mode is also relaunched in the background immediately after system boot to ensure that the VoIP services are always available.
The code below shows that how to configure app's socket for VoIP usage.
Step 1: Connect to server
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) {
}
The server side code may be in C++ environment, but you can pass the clientSocket
to the Objective-C instance, it is a int
value.
Step 2: Create and config read and write stream
After connected to the server, you need to create a read & write stream based on clientSocket
using CFStreamCreatePairWithSocket()
and set the property of streams with NSStreamNetworkServiceTypeVoIP
.
Define read and write streams and keep the strong reference them. Close and release them when the connection is lost.
@property (nonatomic, strong) NSInputStream *inputStream ;
@property (nonatomic, strong) NSOutputStream *outputStream ;
Then config streams:
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] ;
Make sure the socket is already connected before connecting with read & write stream.
Step 3:keep the connection
[[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
}] ;
When your app enters background, the socket is managed by system and when the server sends packet to your app, system wakes it up, passes the packet to it. You only have a few seconds to handle the data, so don't do much work here. As it's a VoIP app, the socket should be used to notify user that a call is incoming and you can push a local notification to make user be aware of that.
Because VoIP apps need to stay running in order to receive incoming calls, the system automatically relaunches the app if it exits with a nonzero exit code. (This type of exit could happen when there is memory pressure and your app is terminated as a result.) However, terminating the app also releases all of its sockets, including the one used to maintain the VoIP service connection. Therefore, when the app is launched, it always needs to create its sockets from scratch.
I have created an example project here, the relevant server side code here.