关于在iOS设备上探测WIFI,3G,GPRS使用情况的细节
转自:http://blog.csdn.net/devfun/article/details/7365774
由于设计的游戏需要有一些联网请求,但有时候在设备未连接网络的情况下,如果对网络情况不加以判断,则可能造成游戏为了等游戏超时,浪费不必要的时间。
所以在游戏启动时检测一下网络状况是很必要的,而且当玩家的设备连接上网络以后,有一个回调函数也是非常必要的一件事儿,这样可能更方便我们在后台下载数据库等信息。
apple为我们提供了一套范例代码,下面我就来分析一下这段代码。
范例代码地址在:https://developer.apple.com/library/ios/#samplecode/Reachability/Listings/Classes_ReachabilityAppDelegate_m.html#//apple_ref/doc/uid/DTS40007324-Classes_ReachabilityAppDelegate_m-DontLinkElementID_4
范例中包含两部分,一部分是appdelegate,一部分是reachability
1 #import <sys/socket.h> 2 #import <netinet/in.h> 3 #import <netinet6/in6.h> 4 #import <arpa/inet.h> 5 #import <ifaddrs.h> 6 #import <netdb.h> 7 8 #import <CoreFoundation/CoreFoundation.h> 9 10 #import "Reachability.h" 11 12 #define kShouldPrintReachabilityFlags 1 13 14 static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) 15 { 16 #if kShouldPrintReachabilityFlags 17 18 NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", 19 (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',//当前网络为蜂窝网络,即3G或者GPRS 20 (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',//网络请求地址可达 21 22 (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', 23 (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', 24 (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', 25 (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', 26 (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', 27 (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',//该值为一个本地地址 28 (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', 29 comment 30 ); 31 #endif 32 } 33 34 35 @implementation Reachability 36 static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) 37 { 38 #pragma unused (target, flags) 39 NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); 40 NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); 41 42 //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively 43 // in case someon uses the Reachablity object in a different thread. 44 NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init]; 45 46 Reachability* noteObject = (Reachability*) info; 47 // Post a notification to notify the client that the network reachability changed. 48 [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; 49 50 [myPool release]; 51 } 52 53 54 55 //启动探测网络 56 - (BOOL) startNotifier 57 { 58 BOOL retVal = NO; 59 SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; 60 //SCNetworkReachabilitySetCallback函数为指定一个target(此处为reachabilityRef,即www.apple.com,在reachabilityWithHostName里设置的) 61 //当设备对于这个target链接状态发生改变时(比如断开链接,或者重新连上),则回调reachabilityCallback函数, 62 if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) 63 { 64 //指定一个runloop给指定的target 65 if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) 66 { 67 retVal = YES; 68 } 69 } 70 return retVal; 71 } 72 //停止网络探测 73 - (void) stopNotifier 74 { 75 if(reachabilityRef!= NULL) 76 { 77 SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 78 } 79 } 80 81 - (void) dealloc 82 { 83 [self stopNotifier]; 84 if(reachabilityRef!= NULL) 85 { 86 CFRelease(reachabilityRef); 87 } 88 [super dealloc]; 89 } 90 //通过域名设定一个目标地址比如www.apple.com 91 + (Reachability*) reachabilityWithHostName: (NSString*) hostName; 92 { 93 Reachability* retVal = NULL; 94 SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); 95 if(reachability!= NULL) 96 { 97 retVal= [[[self alloc] init] autorelease]; 98 if(retVal!= NULL) 99 { 100 retVal->reachabilityRef = reachability; 101 retVal->localWiFiRef = NO; 102 } 103 } 104 return retVal; 105 } 106 //通过ip地址设定一个目标的地址,可以加端口号,貌似我实验的不太成功 107 + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; 108 { 109 SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); 110 Reachability* retVal = NULL; 111 if(reachability!= NULL) 112 { 113 retVal= [[[self alloc] init] autorelease]; 114 if(retVal!= NULL) 115 { 116 retVal->reachabilityRef = reachability; 117 retVal->localWiFiRef = NO; 118 } 119 } 120 return retVal; 121 } 122 //检测当前网络能否连上internet 123 + (Reachability*) reachabilityForInternetConnection; 124 { 125 struct sockaddr_in zeroAddress; 126 bzero(&zeroAddress, sizeof(zeroAddress)); 127 zeroAddress.sin_len = sizeof(zeroAddress); 128 zeroAddress.sin_family = AF_INET; 129 return [self reachabilityWithAddress: &zeroAddress]; 130 } 131 //检测当前网络是否能够连上本地wifi 132 + (Reachability*) reachabilityForLocalWiFi; 133 { 134 struct sockaddr_in localWifiAddress; 135 bzero(&localWifiAddress, sizeof(localWifiAddress)); 136 localWifiAddress.sin_len = sizeof(localWifiAddress); 137 localWifiAddress.sin_family = AF_INET; 138 // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0 139 localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); 140 Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; 141 if(retVal!= NULL) 142 { 143 retVal->localWiFiRef = YES; 144 } 145 return retVal; 146 } 147 148 #pragma mark Network Flag Handling 149 150 - (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags 151 { 152 PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); 153 154 BOOL retVal = NotReachable; 155 if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) 156 { 157 retVal = ReachableViaWiFi; 158 } 159 return retVal; 160 } 161 //通过flags返回当前网络状态 162 - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags 163 { 164 PrintReachabilityFlags(flags, "networkStatusForFlags"); 165 if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) 166 { 167 // if target host is not reachable 168 return NotReachable; 169 } 170 171 BOOL retVal = NotReachable; 172 173 if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) 174 { 175 // if target host is reachable and no connection is required 176 // then we'll assume (for now) that your on Wi-Fi 177 retVal = ReachableViaWiFi; 178 } 179 180 181 if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || 182 (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) 183 { 184 // ... and the connection is on-demand (or on-traffic) if the 185 // calling application is using the CFSocketStream or higher APIs 186 187 if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) 188 { 189 // ... and no [user] intervention is needed 190 retVal = ReachableViaWiFi; 191 } 192 } 193 194 if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) 195 { 196 // ... but WWAN connections are OK if the calling application 197 // is using the CFNetwork (CFSocketStream?) APIs. 198 retVal = ReachableViaWWAN; 199 } 200 return retVal; 201 } 202 203 - (BOOL) connectionRequired; 204 { 205 NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); 206 SCNetworkReachabilityFlags flags; 207 if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 208 { 209 return (flags & kSCNetworkReachabilityFlagsConnectionRequired); 210 } 211 return NO; 212 } 213 //获得当前网络状态 214 - (NetworkStatus) currentReachabilityStatus 215 { 216 NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); 217 NetworkStatus retVal = NotReachable; 218 SCNetworkReachabilityFlags flags; 219 if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 220 { 221 if(localWiFiRef) 222 { 223 retVal = [self localWiFiStatusForFlags: flags]; 224 } 225 else 226 { 227 retVal = [self networkStatusForFlags: flags]; 228 } 229 } 230 return retVal; 231 } 232 @end
以上的部分为具体实现的部分,下面我们再来看在appdelegate里面调用的部分
- (void) applicationDidFinishLaunching: (UIApplication* )application { #pragma unused(application) contentView.backgroundColor = [UIColor groupTableViewBackgroundColor]; summaryLabel.hidden = YES; //这句是设定一个回调函数reachability,当网络条件发生变化时则去调用reachabilityChanged [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil]; //Change the host name here to change the server your monitoring remoteHostLabel.text = [NSString stringWithFormat: @"Remote Host: %@", @"www.apple.com"]; //设定target地址,这里是www.apple.com hostReach = [[Reachability reachabilityWithHostName: @"www.apple.com"] retain]; //启动网络条件检测 [hostReach startNotifier]; //更新UI的网络状态显示 [self updateInterfaceWithReachability: hostReach]; //探测是否能连上internet网 internetReach = [[Reachability reachabilityForInternetConnection] retain]; [internetReach startNotifier]; [self updateInterfaceWithReachability: internetReach]; //探测wifi是否开启 wifiReach = [[Reachability reachabilityForLocalWiFi] retain]; [wifiReach startNotifier]; [self updateInterfaceWithReachability: wifiReach]; [window makeKeyAndVisible]; } //如需要查看网络状态,如下调用即可 NetworkStatus netStatus = [curReach currentReachabilityStatus]; netStatus有ReachableViaWWAN(蜂窝网络3G之类的),ReachableViaWiFi(wifi连接),NotReachable(无法连接)