當iphone應用程序進行網絡編程時,切到後臺後,socket鏈接會斷掉,ios的設計就是這樣。html
可是好在apple公司也沒有那麼絕,仍是有一些東西能夠在後臺運行的(backgroundmodes),ios
好比:音樂 GPS Voip Locationupdates等編程
咱們以voip爲例:服務器
這裏咱們能夠將NSStream指定voip的屬性,從而能夠避免程序切到後臺的時候socket鏈接中斷。網絡
能夠分爲兩步:app
1.在info.plist文件中,增長voip選項,如iphone
2. 設置NSStream的屬性,如socket
在IOS中,sockets是用流或者更高級的結構,設置一個VOIP的socket,你只須要在一般的設置中添加一個特殊的key來標明這個接口是用於鏈接VOIP服務的,下表列出了流的接口和設置:
async
設置流接口用於voip接口spa
NSInputStream 和NSOutputStream 對於 Cocoa streams, 使用 setProperty:forKey: 方法添加
NSStreamNetworkServiceType 屬性給 stream. 改屬性的值設爲 NSStreamNetworkServiceTypeVoIP.
[readStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
這樣,當程序切到後臺的時候,這個socket鏈接還會被保持。
另外,iphone都是經過wifi或者gprs上網的,那麼當socket鏈接空閒一段時間後,這個鏈接有可能被路由器關閉,爲了保持鏈接,咱們須要不停發送'心跳包'(保持鏈接狀態)。
因爲iphone上的程序切到後臺後,程序會被掛起,那麼也就沒法定時發送心跳包,因此這個問題只能由服務端來解決。普通的辦法就是服務器每隔必定時間給每一個客戶端發送一個心跳包,以維持這個鏈接。每當客戶端接收到心跳包的時候,客戶端會被IOS喚醒,得到一小段CPU時間,而後再次進入掛起狀態。
解決方法:
經過設置如下屬性能夠保持socket鏈接和數據的繼續傳輸
1.須要在Info.plist文件中添加UIBackgroundModes中的VOIP鍵值;
2.設置流屬性
CFReadStreamRef和CFWriteStreamRef經過以下方法設置kCFStreamNetworkServiceType屬性爲kCFStreamNetworkServiceTypeVoIP;
CFReadStreamSetProperty(theReadStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(theWriteStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
NSInputStream 和NSOutputStream經過以下方法設置NSStreamNetworkServiceType屬性爲NSStreamNetworkServiceTypeVoIP;
[self.stream setProperty: NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
3.這裏有一個問題,就是客戶端是經過心跳來和服務端保持鏈接,心跳是由定時器觸發的,當我退到後臺之後,定時器方法被掛起,那麼經過以下設置來在後臺運行定時器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- (
void
)applicationDidEnterBackground:(UIApplication *)application{
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if
(bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0
), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if
(bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
}
});
});
}
|