2011-05-13 76 views
20

我的應用程序要求:由於某些原因,我應該維護套接字連接以在服務器推送時觸發本地通知,而無需使用推送通知(APN)。所以我使用iPhone的VOIP後臺功能來維護套接字連接。如何在後臺維護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.我應該重新在處理程序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.說出我的服務器重新啓動和應用程序在後臺,我如何確保連接? 如果我的iPhone中的Wi-Fi連接或如果我終止服務器應用程序,連接將被關閉,那麼應該採取什麼措施使我的應用程序按預期工作?

+1

你在這裏包含的代碼是它的工作代碼嗎? – 2014-02-06 07:15:00

回答

19

你還需要確保你已經設置你的plist文件

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

插座將通過iOS的,而你的應用程序是在後臺進行管理。只要套接字中有可用數據,應用程序就會收到CPU時間。所以在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; 
}]; 

這是確保應用程序不會在發送前被暫停需要ping並等待來自服務器的ping響應。另外,如果套接字已經斷開連接,則可能發生需要重新連接的過程,這需要一些時間並需要在後臺運行。

但是,確保在不再需要後臺時正確釋放後臺任務。當超過bg超時時,其他明智的應用程序將被系統殺死。

+0

剛剛偶然發現一篇文章說,一個應用程序被拒絕,因爲它沒有提供任何IP語音服務,但使用VoIP背景模式。對此有何想法?因此,如果我正在開發即時消息應用程序,並且即使在後臺也希望套接字仍然活着,即使我不支持任何語音功能,我是否仍然可以使用上述方法? – Roshit 2012-04-19 20:41:28

+0

您是否已批准您的應用程序? - 我處於類似的情況,但幸運的是我可以使用企業分佈 – gheese 2012-10-05 14:22:59

+0

也對此感到好奇。如果你有一些故事,請留言! – Nailer 2013-02-25 21:37:47

2

蘋果已經詳細介紹了國際上這個官documentation.You可以在這裏找到https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html

按照文檔

有用於實施VoIP應用幾個要求:

1.將UIBackgroundModes鍵添加到您應用的Info.plist文件中。將此密鑰的值設置爲包含voip字符串的數組。

2.爲VoIP使用配置其中一個應用套接字。

3.在移動到後臺前,調用setKeepAliveTimeout:handler:方法來安裝一個定期執行的處理程序。您的應用可以使用此處理程序來維護其服務連接。

4.配置您的音頻會話以處理往返使用中的轉換。

5.爲確保iPhone上更好的用戶體驗,請使用核心電話框架來調整您的行爲與基於蜂窩電話的關係;請參閱核心電話框架參考。

6.爲確保VoIP應用的良好性能,請使用系統配置框架檢測網絡更改並儘可能讓您的應用休眠。

在UIBackgroundModes鍵中包含voip值可讓系統知道它應該允許應用程序根據需要在後臺運行以管理其網絡套接字。此鍵還允許您的應用播放背景音頻(儘管仍然鼓勵包含UIBackgroundModes鍵的音頻值)。帶有此密鑰的應用程序在系統引導後立即在後臺重新啓動,以確保VoIP服務始終可用。有關UIBackgroundModes鍵的更多信息,請參閱信息屬性列表鍵參考。