Configuring iOS VoIP application to run in sleep/background mode
Asked Answered
T

2

7

I am developing a VoIP based iOS (7.1) application. It's underlying socket programming is written in C++ rather than objective C. The app runs fine in foreground but while putting in sleep/background mode it can't receive any communication from server. According to apple document we have to configure one of the app socket for VoIP usage. I am unable to figure out how to configure C++ sockets (Because there are many sockets; i.e. SSL, SIP, RESTful).

The intention is to run the app in sleep mode until it's killed. Tried few links and even couple of them from SO, but since I am novice I would want to have a step by step procedure for this configuration. [Note: In somewhere I found CoreFoudation framework, do I need to use that?]

Telluride answered 24/12, 2014 at 5:2 Comment(2)
Please post the symbolicated stack trace and error message from your crash log.Smitt
@AaronBrager- Thanks for your reply. I have used [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ }]; this handler is for long running task in background. I know from iOS 7, iOS is allowing upto 3 mins. We should call [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; in inside of handler. Why i am getting crash is i am not calling endBackgroundTask function. I am trying to allowing tasks in background. Thats why i am receiving crash. Our main aim is to do configure socket for Voip usage.Telluride
R
14

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:

  1. 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.
  2. Configure one of the app’s sockets for VoIP usage.
  3. 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.
  4. Configure your audio session to handle transitions to and from active use.
  5. 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.
  6. 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.

Rogovy answered 25/12, 2014 at 1:41 Comment(10)
Thanks for your reply. Actually all the sockets are written using C/C++ <socket.h> library and we are not in a position to change it for now. Will your solution apply to this situation and Can you please elaborate your answer?Telluride
@VinayPodili You should pass the socket out after connect success, and create a read and write stream using CFStreamCreatePairWithSocket function then config the streams.Rogovy
@Rogovy Hi, I have semi successfully implemented your code into my project. The one problem I am having is that after 3-10 minutes, the data that I am sending from the server takes up to 2 minutes to arrive at the client. Do you have experience with this, or an explanation maybe? Thanks.Cordierite
@Cordierite No, I didn't have experience with this. I can have a try on my computer tomorrow morning.Rogovy
@Rogovy BTW, we are testing this on an Ipad 3 in sleep mode, as of now it seems it is occurring with Wi-Fi.Cordierite
@Cordierite I have tested that and it works well here, I don't know what is happening there. You can use my client and server project to test, if there is the same issue, leave me a message. :)Rogovy
@Rogovy Thanks for taking your time and testing it out! We have also used your project and we seem to be having the same problem. We are now testing to see if it's our firewall. Still talking about the background socket in sleep mode BTW.Cordierite
@Cordierite Can you have a try to select the second cell in the main page of my project ? That is implemented by CFStream, not C socket.Rogovy
@Rogovy We have come to the conclusion that it was our firewall that was delaying/blocking incoming packets. Thanks for your assistance!Cordierite
Thanks guys ..yours answers and comments helped me alot :)Telluride
S
2

On top of configuring socket you need to make some changes to iOS app Info.plist file where you should specify voip background mode.

More details can be found here: Apple Dev Documentation

Succumb answered 24/12, 2014 at 14:59 Comment(5)
Thanks for your reply. Yes i have enabled voip background mode in info.plist I am guessing need to do configuring socket. Do i need to use CFStreamCreatePairWithSocket for configuring socket?Telluride
I don't think that you need any specic class to do configuration, just use setTimeoutInterval:handler: as apple doc suggests. AFAIK Your socket will be closed periodically, you will need to reconnectSuccumb
Thanks. So Don't i need to set kCFStreamNetworkServiceTypeVoIP? And i have multiple sockets which are running on threads. In iOS When ever we go to background, application will be suspended. So socket connection will be lost. I want to keep Alive socket connection for long time in sleep mode.Telluride
Hi Vinay, to keep alive socket connection you need to use this method: NSApplication setKeepAliveTimeout:handler:. This should give you an ability to control how long socket will be kept open in background.Succumb
"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." So you must "Configure one of the app’s sockets for VoIP usage." or you can't receive the incoming call. NSApplication setKeepAliveTimeout:handler: is used to maintain its service connection , for example: send keep-live packet to server.Rogovy

© 2022 - 2024 — McMap. All rights reserved.