當iphone應用程序進行網絡編程時,切到后台后,socket連接會斷掉,ios的設計就是這樣。
但是好在apple公司也沒有那么絕,還是有一些東西可以在后台運行的(backgroundmodes),

比如:音樂 GPS Voip Locationupdates等
我們以voip為例:
這里我們可以將NSStream指定voip的屬性,從而可以避免程序切到后台的時候socket連接中斷。
可以分為兩步:
1.在info.plist文件中,增加voip選項,如

2. 設置NSStream的屬性,如
在IOS中,sockets是用流或者更高級的結構,設置一個VOIP的socket,你只需要在通常的設置中添加一個特殊的key來標明這個接口是用於連接VOIP服務的,下表列出了流的接口和設置:
設置流接口用於voip接口
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;
}
});
});
}
|