Вы также должны убедиться, что вы установили в своем файле pList
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
Сокетом будет управлять iOS, пока ваше приложение находится в фоновом режиме.Ваше приложение получит процессорное время, как только в сокете появятся данные.Итак, в runLoop я проверяю ht
. В моем случае протокол сигнализации работает в отдельном потоке, поэтому я запускаю runLoop самостоятельно
// Start runloop
while (!m_needStop)
{
CFRunLoopRun();
}
и останавливаю его при необходимости:
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
}
}
Чтобы сервер знал, что клиент еще жив, мы отправляем команду ping на сервер каждые 10 минут, чтобы обработчик KeepAlive был установлен на 600. ВыМожно использовать другие значения для экономии заряда батареи, но это ухудшит обнаружение разъединений на стороне клиента и сервера.И увеличит время между отключением и повторным подключением.
BOOL scheduled = [app setKeepAliveTimeout:pingTimeout handler:^{ // Schedule processing after some time interval
SchedulePing(0);
}
Где SchedulePing (0) будет выполняться следующим образом:
StartLongBGTask();
if ( avoidFinishBgTask != NULL )
*avoidFinishBgTask = true;
m_pingTimer = CreateTimer(pingTimeout, PingTimerCallback); // result is ignored
И StartLongBGTask - это
m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:m_bgTask];
m_bgTask = UIBackgroundTaskInvalid;
}];
Это необходимо, чтобы убедиться, что приложение не будет приостановлено до отправки пинга и ожидания ответа на пинг с сервера.Также, если сокет уже отключен, может случиться так, что потребуется повторное подключение, которое займет некоторое время и требует выполнения процесса в фоновом режиме.
Но убедитесь, что фоновые задачи должным образом освобождены, когда вам больше не нужноих.Другое мудрое приложение будет уничтожено системой при превышении времени ожидания bg.