iphone开发——稽查网络状态

iphone开发——检查网络状态

 

 

iPhone SDK中附带了 SCNetworkReachabilityCreateWithAddress用于检查ip地址是否可以访问。传入清零地址(0.0.0.0)可用来查询网络状态。
网络可使用时返回YES,否则为NO。

返回标记:
kSCNetworkReachabilityFlagsIsWWAN    :测试用户使用的时运营商的网络还是本地wifi。
kSCNetworkFlagsConnectionRequired:无需更多链接。
kSCNetworkFlagsReachable:表明网络可以访问。


代码如下:

头文件:
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
#import <SystemConfiguration/SCNetworkReachability.h>


frameworks:/System/Library/Frameworks/SystemConfiguration.framework


- (BOOL) connectedToNetwork
{
    // Create zero addy
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    // Recover reachability flags
    SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
    SCNetworkReachabilityFlags flags;

    BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
    CFRelease(defaultRouteReachability);

    if (!didRetrieveFlags)
    {
        return NO;
    }

    BOOL isReachable = flags & kSCNetworkFlagsReachable;
    BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
    return (isReachable && !needsConnection) ? YES : NO;
}

//call like:
-(void) start {
    if (![self connectedToNetwork]) {
                UIAlertView *alert = [[UIAlertView alloc] 
                         initWithTitle:@"Network Connection Error" 
                         message:@"You need to be connected to the internet to use this feature." 
                         delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];
        } else {
             //do something 
        }
}

使用上述方法可能会出现以下问题:

当使用者在使用你的应用程式的时候,如果关掉萤幕,将装置放在锁定状态(就是那个重新打开时需要在萤幕上画一下「解锁」的状态)一阵子,然后再按一下按钮恢复使用,这个时候你想要做一些网路操作,其实是可以连线,但是Reachability API 还是告诉你无法连线;或刚打开应用程式的时候,也告诉你无法连线。当你需要判断能不能连线,使用者用了哪种连线而应不应该继续连线的时候,其实使用者可以连线,API 却始终一直告诉你不能连线。

把装置设定为锁定,然后恢复使用的状况是这样的-苹果的设计是,为了节省电力消耗,会在进入锁定状态后,自动关闭无线网路介面,而当你解除锁定后,才会把无线网路再度打开。而Reachability 基本上只询问「目前的网路状态」,如果装置的无线网路正处于从关闭的状态恢复的阶段,这时后回传的结果便是无法连线。

顺道一提,在第三方应用程式中,可以在Info.plist 档案中,设定UIRequiresPersistentWifi 这个选项,文件中说这个设定可以让应用程式持续保持无线网路的连线状态,但是,就算设了这项设定,在进入锁定状态后,系统仍然会自动关闭无线网路介面,这项设定仅局限于iPhone 一直开着、你不去把萤幕关掉的状况。

至于怎样在锁定状态下继续保持连线,那又是另外一个话题了。

刚进入应用程式的时候,也往往回传无法连线-猜测应该是使用iPhone 主画面(Springboard)的时候,无线网路介面大概也是关闭的。在点选要用什么应用程式的时候,好像也用不到什么网路功能,为了节电把网路介面关了也好。至于定时检查信箱、或从AppStore 下载软体什么的,应该是苹果有其他自己的背景程序,负责呼叫网路介面。

简言之,就是你常会遇到「问的时候说没有,但是下一秒钟网路就通」的状况,遇到这种状况,要使用比较白烂的作法,可以向Reachability 连续问两次,如果第一次说没有第二次却说有,那就代表其实还是有网路…可能比较好的作法,可以是,我们不要叫一个method 直接回传给我们网路状态,而是变成delegate 的方式来处理。

流程大概是这样,这边稍微有些啰嗦-

   1. 设计两个delegate method,分别用于有网路与没网路两种状况。
   
2. 先生出一个SCNetworkReachabilityRef 物件,然后用SCNetworkReachabilityGetFlags() 抓取目前的网路状态,如果是有,就呼叫「有网路」的那组delegate method,直接结束。
   3. 如果这一次抓取网路状态的结果是没有连线,我们就对刚刚产生的SCNetworkReachabilityRef 物件设定一个SCNetworkReachabilityCallBack function。因为只要连线状态出现变化,就会呼叫这个function,所以,在呼叫到的时候,再用SCNetworkReachabilityGetFlags() 抓一次目前的网路状态,决定要回传是「有网路」或「没网路」的delegate method。如果有呼叫到,通常是会有,如此一来,我们可以捕捉到了「第一次说没有,但是后来又有网路」的状况,并且成功回传「有网路」。
   
4. 同时设一组timer(或用NSObject 的perform selector after delay 之类的),如果超过一段时间,SCNetworkReachability API 都没有被呼叫前一点中提到的SCNetworkReachabilityCallBack function,就代表不但一开始没网路,而且后来一直还是那个状态,那…就代表一直没网路。这时后呼叫「没网路」的delegate method。
   5. 记得要release 那个SCNetworkReachabilityRef 物件…。

采用这种实作,在没有网路连线的状态下,就会需要几秒钟的等待,确定目前的确没有网路连线。不过嘛,反正没有网路连线,也不能够做什么别的事情,所以等个几秒钟也无所谓嘛。

顺道一提,iPhone SDK 关于SCNetworkReachability 的说明文件里头有个错误,在讲如何设定SCNetworkReachabilityCallBack 的部份提到-

Here is an example of a function declared to conform to this type:

- (void)MyReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);

 

另外一篇学习文章:

http://www.yifeiyang.net/iphone-web-development-skills-of-the-article-4-make-sure-the-network-environment-3gwifi/

 

apple的demo:Reachability