- 没事看看这个网络环境监听的东西,待会还要去学英语呢
- 我看的这个是属于ASI框架的
- 大概看了一下,最核心的判断网络的方法是这个
- SCNetworkReachabilityGetFlags
- 这个c函数获取到各种flag,它是系统SystemConfiguration框架里SCNetworkReachability.h里的一个方法
看它的介绍
123456789101112/*!@function SCNetworkReachabilityGetFlags@discussion Determines if the given target is reachable using thecurrent network configuration.@param target The network reference associated with the address or nameto be checked for reachability.@param flags A pointer to memory that will be filled with theSCNetworkReachabilityFlags detailing the reachabilityof the specified target.@result Returns TRUE if the network connection flags are valid;FALSE if the status could not be determined.*/大概意思就是根据传入的target(SCNetworkReachabilityRef)来得到当前网络状态
让我们看看得到的flag分别怎么处理
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182- (NetworkStatus)networkStatusNewForFlags:(SCNetworkReachabilityFlags)flags{if ((flags & kSCNetworkReachabilityFlagsReachable) == 0){return NotReachable;}NetworkStatus retVal = NotReachable;if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0){// if target host is reachable and no connection is required// then we'll assume (for now) that your on Wi-FiretVal = ReachableViaWiFi;}if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)){// ... and the connection is on-demand (or on-traffic) if the// calling application is using the CFSocketStream or higher APIsif ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0){// ... and no [user] intervention is neededretVal = ReachableViaWiFi;}}if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN){if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;if (currentRadioAccessTechnology) {if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {return kReachableVia4G;} else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {return kReachableVia2G;} else {return kReachableVia3G;}}}if ((flags & kSCNetworkReachabilityFlagsTransientConnection) == kSCNetworkReachabilityFlagsTransientConnection) {if((flags & kSCNetworkReachabilityFlagsConnectionRequired) == kSCNetworkReachabilityFlagsConnectionRequired) {return kReachableVia2G;}return kReachableVia3G;}return kReachableViaWWAN;}return retVal;}上面这里将一个方法,一个生产flag的东西当做参数传了进去
- 很明显,利用runloop进行网络的环境监听
- 当有变动时,则会调用ReachabilityCallback函数
这个函数也是有讲究的
123456789101112131415/*!@typedef SCNetworkReachabilityCallBack@discussion Type of the callback function used when thereachability of a network address or name changes.@param target The SCNetworkReachability reference being monitoredfor changes.@param flags The new SCNetworkReachabilityFlags representing thereachability status of the network address/name.@param info A C pointer to a user-specified block of data.*/typedef void (*SCNetworkReachabilityCallBack) (SCNetworkReachabilityRef target,SCNetworkReachabilityFlags flags,void * __nullable info);看它说明
the callback function used when the reachability of a network address or name changes
为网络改变而生的回调,哈哈
再来看看这个设置callback的方法
12345678910111213141516171819202122SCNetworkReachabilitySetCallback//////////////*!@function SCNetworkReachabilitySetCallback@discussion Assigns a client to a target, which receives callbackswhen the reachability of the target changes.@param target The network reference associated with the address orname to be checked for reachability.@param callout The function to be called when the reachability of thetarget changes. If NULL, the current client for the targetis removed.@param context The SCNetworkReachabilityContext associated withthe callout. The value may be NULL.@result Returns TRUE if the notification client was successfully set.*/BooleanSCNetworkReachabilitySetCallback (SCNetworkReachabilityRef target,SCNetworkReachabilityCallBack __nullable callout,SCNetworkReachabilityContext * __nullable context) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);所以,你也可以自己写个网络环境监听,知道系统方法干啥的就行了
最后别忘了释放监听
1234567- (void)stopNotifier{if (_reachabilityRef != NULL){SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);}}细看就可以大概了解这些flag是怎么运作的
- 接下来看看是如何做到实时的网络环境监听的123456789101112131415- (BOOL)startNotifier{BOOL returnValue = NO;SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)){if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)){returnValue = YES;}}return returnValue;}
- 这里附上一个监测ipv6的东西
这里先贴上两个工具方法
1234567891011121314151617181920212223242526272829303132/*** @brief 判断是否为IPv4地址** @return*/- (BOOL)isIPv4Address:(NSString *)addr {if (addr == nil) {return NO;}const char *utf8 = [addr UTF8String];// Check valid IPv4.struct in_addr dst;int success = inet_pton(AF_INET, utf8, &(dst.s_addr));return success == 1;}/*** @brief 判断是否为IPv6地址** @return*/- (BOOL)isIPv6Address:(NSString *)addr {if (addr == nil) {return NO;}const char *utf8 = [addr UTF8String];// Check valid IPv6.struct in6_addr dst6;int success = inet_pton(AF_INET6, utf8, &dst6);return (success == 1);}然后再进行下面的逻辑运算
1234567891011121314dnsIPStack = IPLocalIPStack_IPv4 | IPLocalIPStack_IPv6;dnsIPStack &= [self getDNSServersIpStack];if (dnsIPStack & IPLocalIPStack_IPv4) {// support IPv4isIPv6Only = NO;} else if (dnsIPStack & IPLocalIPStack_IPv6) {// IPv6-OnlyisIPv6Only = YES;[[OSSIPv6PrefixResolver getInstance] updateIPv6Prefix];} else {KGIPv6Log(@"[%s]: Error.", __FUNCTION__);isIPv6Only = NO;}其中如果是ipv6我们就要做些事情了,不过先贴上完整代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566- (BOOL)isIPv6OnlyNetwork {@synchronized(self) {if (isIPv6OnlyResolved) {return isIPv6Only;}struct in6_addr addr6_gateway = {0};if (0 != getdefaultgateway6(&addr6_gateway) || IN6_IS_ADDR_UNSPECIFIED(&addr6_gateway)) {isIPv6Only = NO;return isIPv6Only;}struct in_addr addr_gateway = {0};if (0 != getdefaultgateway4(&addr_gateway)) {isIPv6Only = YES;[[OSSIPv6PrefixResolver getInstance] updateIPv6Prefix];return isIPv6Only;}if (INADDR_NONE == addr_gateway.s_addr|| INADDR_ANY == addr_gateway.s_addr ) {isIPv6Only = YES;[[OSSIPv6PrefixResolver getInstance] updateIPv6Prefix];return isIPv6Only;}int haveIPv4 = _have_ipv4();int haveIPv6 = _have_ipv6();int local_stack = 0;if (haveIPv4) { local_stack |= IPLocalIPStack_IPv4; }if (haveIPv6) { local_stack |= IPLocalIPStack_IPv6; }if (IPLocalIPStack_IPv4 == local_stack) {isIPv6Only = NO;return isIPv6Only;} else if (IPLocalIPStack_IPv6 == local_stack) {isIPv6Only = YES;[[OSSIPv6PrefixResolver getInstance] updateIPv6Prefix];return isIPv6Only;}KGIPv6Log(@"Start resolved network to see if in IPv6-Only env.");int dnsIPStack = 0;dnsIPStack = IPLocalIPStack_IPv4 | IPLocalIPStack_IPv6;dnsIPStack &= [self getDNSServersIpStack];if (dnsIPStack & IPLocalIPStack_IPv4) {// support IPv4isIPv6Only = NO;} else if (dnsIPStack & IPLocalIPStack_IPv6) {// IPv6-OnlyisIPv6Only = YES;[[OSSIPv6PrefixResolver getInstance] updateIPv6Prefix];} else {KGIPv6Log(@"[%s]: Error.", __FUNCTION__);isIPv6Only = NO;}isIPv6OnlyResolved = YES;if (isIPv6Only) {KGIPv6Log(@"[%s]: IPv6-Only network now.", __FUNCTION__);} else {KGIPv6Log(@"[%s]: Not IPv6-Only network now.", __FUNCTION__);}return isIPv6Only;}}下面我们看一下是如何判断当前网络是否为ipv6only的
答案是查询本机DNS Servers协议
1234567891011121314151617181920212223242526- (int)getDNSServersIpStack {int dns_stack = 0;res_state res = malloc(sizeof(struct __res_state));int result = res_ninit(res);if (result == 0) {union res_9_sockaddr_union *addr_union = malloc(res->nscount * sizeof(union res_9_sockaddr_union));res_getservers(res, addr_union, res->nscount);for (int i = 0; i < res->nscount; i++) {if (addr_union[i].sin.sin_family == AF_INET) {char ip[INET_ADDRSTRLEN];if (inet_ntop(AF_INET, &(addr_union[i].sin.sin_addr), ip, INET_ADDRSTRLEN)) {dns_stack |= IPLocalIPStack_IPv4;}} else if (addr_union[i].sin6.sin6_family == AF_INET6) {char ip[INET6_ADDRSTRLEN];if (inet_ntop(AF_INET6, &(addr_union[i].sin6.sin6_addr), ip, INET6_ADDRSTRLEN)) {dns_stack |= IPLocalIPStack_IPv6;}} else {KGIPv6Log(@"%s: Undefined family.", __FUNCTION__);}}}res_nclose(res);return dns_stack;}我们看看这个OSSIPv6PrefixResolver是如何解决ipv6问题的
直接就是下面的转换,不过还需要一个函数解决prefix的问题,代码下面附上
123456789if (([[KGIPv6Adapater sharedInstance] isIPv6OnlyNetwork]&& [[UIDevice currentDevice].systemVersion floatValue] < 9.0)) {NSURL *tempURL = [NSURL URLWithString:domainURLString];if ([[KGIPv6Adapater sharedInstance] isIPv4Address:tempURL.host]) {NSString *tempIPv6String = [[KGIPv6Adapater sharedInstance] handleIpv4Address:tempURL.host];domainURLString = [domainURLString stringByReplacingOccurrencesOfString:tempURL.hostwithString:tempIPv6String];}}查看当前是否只允许ipv6网络,是则将ipv4转换成ipv6
下面是转换算法
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354- (NSString *)convertIPv4toIPv6:(NSString *)ipv4 {if ([ipv4 length] <= 0) {return nil;}__uint8_t ipv6[16] = {0x00};__uint8_t length = [self resolveIPv6Prefix:ipv6];if (length <= 0) {return nil;}in_addr_t addr_v4 = inet_addr([ipv4 UTF8String]);// 按length的不同情况进行处理if (length==4 || length==12) { //32 bits or 96 bitsipv6[length+0] |= (__uint8_t)(addr_v4>>0 & 0xff);ipv6[length+1] |= (__uint8_t)(addr_v4>>8 & 0xff);ipv6[length+2] |= (__uint8_t)(addr_v4>>16 & 0xff);ipv6[length+3] |= (__uint8_t)(addr_v4>>24 & 0xff);}else if (length == 5) { //40 bits :a.b.c.0.dipv6[length+0] |= (__uint8_t)(addr_v4>>0 & 0xff);ipv6[length+1] |= (__uint8_t)(addr_v4>>8 & 0xff);ipv6[length+2] |= (__uint8_t)(addr_v4>>16 & 0xff);ipv6[length+4] |= (__uint8_t)(addr_v4>>24 & 0xff);}else if (length == 6) { //48 bits :a.b.0.c.dipv6[length+0] |= (__uint8_t)(addr_v4>>0 & 0xff);ipv6[length+1] |= (__uint8_t)(addr_v4>>8 & 0xff);ipv6[length+3] |= (__uint8_t)(addr_v4>>16 & 0xff);ipv6[length+4] |= (__uint8_t)(addr_v4>>24 & 0xff);}else if (length == 7) { //56 bits :a.0.b.c.dipv6[length+0] |= (__uint8_t)(addr_v4>>0 & 0xff);ipv6[length+2] |= (__uint8_t)(addr_v4>>8 & 0xff);ipv6[length+3] |= (__uint8_t)(addr_v4>>16 & 0xff);ipv6[length+4] |= (__uint8_t)(addr_v4>>24 & 0xff);}else if (length == 8) { //64 bits :0.a.b.c.dipv6[length+1] |= (__uint8_t)(addr_v4>>0 & 0xff);ipv6[length+2] |= (__uint8_t)(addr_v4>>8 & 0xff);ipv6[length+3] |= (__uint8_t)(addr_v4>>16 & 0xff);ipv6[length+4] |= (__uint8_t)(addr_v4>>24 & 0xff);}// 构造IPv6的结构char addr_text[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];if(inet_ntop(AF_INET6, ipv6, addr_text, INET6_ADDRSTRLEN)) {NSString *ret = [NSString stringWithUTF8String:addr_text];return ret;}return nil;}解决ipv6prefix问题
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051- (__uint8_t)resolveIPv6Prefix:(__uint8_t *)prefix {if ( !prefix ) {return 0;}__uint8_t len = prefixLen;memcpy(prefix, ipv6Prefix, prefixLen);@synchronized(self) {if (ipv6PrefixResolveStatus==IPv6PrefixUnResolved ) {ipv6PrefixResolveStatus = IPv6PrefixResolving;dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{struct addrinfo hints, *addr;memset(&hints, 0, sizeof(hints));hints.ai_family = PF_INET6;hints.ai_socktype = SOCK_STREAM;hints.ai_flags = AI_DEFAULT;if (0 != getaddrinfo("ipv4only.arpa", "http", &hints, &addr)) {ipv6PrefixResolveStatus = IPv6PrefixUnResolved;return;}if (addr && AF_INET6 == addr->ai_addr->sa_family) {struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)(addr->ai_addr);if ( !addr6 ) {ipv6PrefixResolveStatus = IPv6PrefixUnResolved;return;}__uint8_t* u8 = addr6->sin6_addr.__u6_addr.__u6_addr8;for (__uint8_t i=0; i < V6_PREFIX_TABLE_SIZE; i++) {if ([self isIPv6Prefix:(__uint8_t *)V6_PREFIX_CONTENT_TABLE[i]withPrefixLen:V6_PREFIX_SIZE_TABLE[i]withIP:u8withIPLen:16]) {ipv6Prefix = (__uint8_t *)V6_PREFIX_CONTENT_TABLE[i];prefixLen = V6_PREFIX_SIZE_TABLE[i];ipv6PrefixResolveStatus = IPv6PrefixResolved;break;}}ipv6PrefixResolveStatus = IPv6PrefixUnResolved;}});}}return len;}ok,本期讲座到此结束