VOIP 以及后台程序相关

必须成对出现的两个函数,而且,就在快要结束前的evnts handler 中调用后者

m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{

    [[UIApplication sharedApplication] endBackgroundTask:m_bgTask];

    m_bgTask = UIBackgroundTaskInvalid;


===== beginBackgroundTaskWithExpirationHandler:

- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handleriOS (4.0 and later)Marks the beginning of a new long-running background task.handler: A handler to be called shortly before the application’s remaining background time reaches 0.

A unique identifier for the new background task.

endBackgroundTask:

Marks the end of a specific long-running background task.

- (void)endBackgroundTask:(UIBackgroundTaskIdentifier) identifier
Parameters
identifier

An identifier returned by the beginBackgroundTaskWithExpirationHandler: method.

Discussion

You must call this method to end a task that was started using thebeginBackgroundTaskWithExpirationHandler: method. If you do not, the system may kill your application.

This method can be safely called on a non-main thread.

Availability
  • Available in iOS 4.0 and later.


========

My App Requirement: I should maintain a socket connection to trigger local notification on server push without using Push Notification(APNs) for some reasons. So i'm using the VOIP background capability of iphone to maintain socket connection.

1. I have configured a stream for VOIP in order to persist socket connection to run in background, so what Timeout value should i set?, will the socket connection terminates once the timeout expires?, how do i make my application to listen to the socket all the time.?

Client stream configuration is as follows,

    NSString *urlStr = @"http://192.168.0.108";
    NSURL *website = [NSURL URLWithString:urlStr];
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream);

    CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
    CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);    

    NSInputStream *inputStream = (NSInputStream *)readStream;
    NSOutputStream *outputStream = (NSOutputStream *)writeStream;
    [inputStream setDelegate:self];
    [inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
    [outputStream setDelegate:self];
    [outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream open];
    [outputStream open];

2. should i reconnect the stream in the handler applicationDidEnterBackground:

   [[UIApplication sharedApplication] setKeepAliveTimeout:86400 handler:^(void) 
{

    if (inputStream)
        [inputStream close];
    if (outputStream)
        [outputStream close];


    urlStr = @"http://192.168.0.108";
    website = [NSURL URLWithString:urlStr];
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream);
    CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
    CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);    
    inputStream = (NSInputStream *)readStream;
    outputStream = (NSOutputStream *)writeStream;
    [inputStream setDelegate:self];
    [inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
    [outputStream setDelegate:self];
    [outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream open];
    [outputStream open];

    }];

3. Say My server restarts and the app is in background how do i ensure the connection? If the Wi-Fi connection in my iphone or if i terminate the server app the connection will be closed, so what measures should i take to make my app work as expection?


You also need to ensure that you have set the in your pList file

<key>UIBackgroundModes</key>
<array>
    <string>voip</string>
</array>

The socket will be managed by the iOS while your application is in the background. Your application will receive the CPU time as soon there is a data available in the socket. So in the runLoop I am checking ht

In my case the signaling protocol is working in a separate thread, so I am spinning the runLoop my self

  // Start runloop
  while (!m_needStop) 
  {
    CFRunLoopRun();
  }

And stopping it when needed:

  m_needStop = true;
  {
    QAutoLock l(m_runLoopGuard);
    if ( m_runLoop != NULL )
      CFRunLoopStop(m_runLoop);
  }

For the sockets in runLoop I have setup the handler functions before scheduling them into the runLoop:

  int nFlags = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered;
  CFStreamClientContext context;
  context.info = this;
  context.version = 0;
  context.release = NULL;
  context.retain = NULL;
  context.copyDescription = NULL;

  if ( !CFReadStreamSetClient(m_readStream, nFlags, NotificationProtocolHandler::ReadStreamCallback, &context) )
  {
    ReleaseStreams();
    return false;
  }

  if ( !CFWriteStreamSetClient(m_writeStream, nFlags, NotificationProtocolHandler::WriteStreamCallback, &context) )
  {
    ReleaseStreams();
    return false;
  }

These are the functions which will be called when your socket will has some info for you and even if your application in the background:

void NotificationProtocolHandler::ReadStreamCallback(CFReadStreamRef stream,
                                                     CFStreamEventType eventType,
                                                     void *clientCallBackInfo)
{      
  NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo;
  switch (eventType)
  {
    case kCFStreamEventOpenCompleted:
      break;

    case kCFStreamEventHasBytesAvailable:
      handler->ProcessInput();
      break;

    case kCFStreamEventErrorOccurred:
      handler->ProcessConnectionError();
      break;

    case kCFStreamEventEndEncountered:
      handler->ProcessConnectionError();
      break;

    default:
      break; // do nothing
  }
}

void NotificationProtocolHandler::WriteStreamCallback(CFWriteStreamRef stream,
                                                      CFStreamEventType eventType,
                                                      void *clientCallBackInfo)
{
  NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo;

  switch (eventType)
  {
    case kCFStreamEventOpenCompleted:
      handler->ProcessOutputConnect();
      break;

    case kCFStreamEventCanAcceptBytes:
      handler->ProcessReadyToWrite();
      break;

    case kCFStreamEventErrorOccurred:
      handler->ProcessConnectionError();
      break;

    case kCFStreamEventEndEncountered:
      handler->ProcessConnectionError();
      break;     

    default:
      break; // do nothing
  }
}

To make server aware that client is still alive we send the ping command to server every 10 minutes so the KeepAlive handler is set to 600. You can use other values to save the battery but it will make worse the detection of the disconnects on the client and server side. And will increase the time between the disconnect and reconnect.

BOOL scheduled = [app setKeepAliveTimeout:pingTimeout handler:^{ // Schedule processing after some time interval      

  SchedulePing(0);
}

Where SchedulePing(0) will be executed as following:

StartLongBGTask();
if ( avoidFinishBgTask != NULL )
  *avoidFinishBgTask = true;
m_pingTimer = CreateTimer(pingTimeout, PingTimerCallback); // result is ignored

And StartLongBGTask is a

m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
  [[UIApplication sharedApplication] endBackgroundTask:m_bgTask];
  m_bgTask = UIBackgroundTaskInvalid;
}];

This is needed to make sure that application will not be suspended before sending the ping and waiting the reply on ping from the server. Also if socket is already disconnected it could happen that reconnect is needed which will take some time and needs proccess to be running in the background.

But make sure to free the background tasks properly when you don't longer need them. Other wise application will be killed by the system when bg timeout will be exceeded.


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值