Iphone 如何在后台维护VOIP套接字连接?

Iphone 如何在后台维护VOIP套接字连接?,iphone,sockets,background,voip,Iphone,Sockets,Background,Voip,我的应用程序要求:出于某些原因,我应该在不使用推送通知(APNs)的情况下维护套接字连接,以触发服务器推送上的本地通知。所以我使用iPhone的VOIP后台功能来维护套接字连接 1。我已经为VOIP配置了一个流,以便将套接字连接保持在后台运行,那么我应该设置什么超时值呢? 套接字连接是否会在超时过期后终止? 如何使我的应用程序始终侦听套接字? 客户端流配置如下: NSString *urlStr = @"http://192.168.0.108"; NSURL *website = [NSURL

我的应用程序要求:出于某些原因,我应该在不使用推送通知(APNs)的情况下维护套接字连接,以触发服务器推送上的本地通知。所以我使用iPhone的VOIP后台功能来维护套接字连接

1。我已经为VOIP配置了一个流,以便将套接字连接保持在后台运行,那么我应该设置什么超时值呢? 套接字连接是否会在超时过期后终止? 如何使我的应用程序始终侦听套接字?

客户端流配置如下:

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。我是否应该在处理程序ApplicationIdentinterBackground中重新连接流:

   [[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。假设我的服务器重新启动且应用程序处于后台,如何确保连接?
如果我的iPhone中有Wi-Fi连接,或者我终止了服务器应用程序,那么连接将被关闭,那么我应该采取什么措施使我的应用程序按照预期工作?

您还需要确保已在pList文件中设置了

<key>UIBackgroundModes</key>
<array>
    <string>voip</string>
</array>
并在需要时停止:

  m_needStop = true;
  {
    QAutoLock l(m_runLoopGuard);
    if ( m_runLoop != NULL )
      CFRunLoopStop(m_runLoop);
  }
对于runLoop中的套接字,在将其调度到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;
  }
这些函数将在套接字为您提供一些信息时调用,即使您的应用程序处于后台:

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
  }
}
为了让服务器知道客户端仍然处于活动状态,我们每10分钟向服务器发送一次ping命令,以便将KeepAlive处理程序设置为600。您可以使用其他值来节省电池,但这会使客户端和服务器端的断开检测变得更糟。并将增加断开和重新连接之间的时间

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

  SchedulePing(0);
}
其中,计划(0)将按以下方式执行:

StartLongBGTask();
if ( avoidFinishBgTask != NULL )
  *avoidFinishBgTask = true;
m_pingTimer = CreateTimer(pingTimeout, PingTimerCallback); // result is ignored
这是一项令人吃惊的任务

m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
  [[UIApplication sharedApplication] endBackgroundTask:m_bgTask];
  m_bgTask = UIBackgroundTaskInvalid;
}];
这是为了确保应用程序在发送ping并等待服务器对ping的回复之前不会被挂起。此外,如果套接字已断开连接,则可能需要重新连接,这将需要一些时间,并且需要在后台运行进程

但是,当您不再需要后台任务时,请确保正确地释放它们。当超过bg超时时,系统将终止其他wise应用程序

苹果已经在官方文档中提供了详细信息。你可以在这里找到

根据文件

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

1.将UIBackgroundModes键添加到应用程序的Info.plist文件中。将此键的值设置为包含voip字符串的数组

2.为VoIP使用配置应用程序的一个套接字

3.在移到后台之前,调用setKeepAliveTimeout:handler:方法来安装要定期执行的处理程序。您的应用程序可以使用此处理程序来维护其服务连接

4.配置音频会话,以处理与活动使用之间的转换

5.为了确保在iPhone上获得更好的用户体验,请使用核心电话框架来调整您的手机通话行为;请参阅核心电话框架参考

6.为确保您的VoIP应用程序具有良好的性能,请使用系统配置框架检测网络更改,并尽可能让您的应用程序睡眠


在UIBackgroundModes键中包含voip值可以让系统知道它应该允许应用程序在后台运行,以管理其网络套接字。此键还允许您的应用程序播放背景音频(尽管仍然鼓励使用UIBackgroundModes键的音频值)。系统启动后,具有此密钥的应用程序也会立即在后台重新启动,以确保VoIP服务始终可用。有关UIBackgroundModes键的更多信息,请参阅信息属性列表键参考。

刚刚偶然发现一篇帖子,其中说某个应用被拒绝,因为它不提供任何IP语音服务,而是使用VoIP后台模式。你知道吗?因此,如果我正在开发一个IM应用程序,并且我希望该套接字即使在后台也能激活,那么即使我不支持任何语音功能,我仍然可以使用上述方法吗?你的应用程序获得批准了吗我也有类似的情况,但幸运的是,我可以使用企业发行版,我对此也很好奇。如果你有一些故事,请插话!您在此处包含的代码是否有效?仅供参考:iOS 10及更高版本现在不推荐使用iOS VOIP后台模式。您好。我想编写一个crm web应用程序。我想集成我的crm应用程序和客户VOIP实时通信。我该如何做?我需要套接字编程吗?
m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
  [[UIApplication sharedApplication] endBackgroundTask:m_bgTask];
  m_bgTask = UIBackgroundTaskInvalid;
}];