iOS保持待续链接

当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;
             }
         });
     });
}