在WWDC2015上苹果宣布iOS9将支持纯IPv6的网络服务。2016年初开始所有提交到App Store的应用必须支持IPv6。这早已不是新鲜事,网上也出了大部分博文供我们参考怎么支持IPv6,关于Reachability,苹果也推出了Version 5.0,用来支持IPv6,但细心的小伙伴可能会发现,最新版的Reachability最低的系统要求是iOS 8.0。
这个对于大部分开发者来说是没有问题的,但是客户非要支持iOS7.0怎么办?小弟我就遇到了这样的客户,赶紧在iOS7.0系统上测试最新的Reachability,希望不要有错,测试代码如下:
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NSLog(@"status:%ld",(long)[reachability currentReachabilityStatus]);
测试结果往往不如所愿,如下图,分别在IPv6、IPv4网络环境下使用iOS9.0以及iOS7.0手机测试,iOS7.0系统的手机在IPv6网络环境下是无法得到这确结果的。(IPv6网络环境搭建请看这里)
故我开始了对Reachability的改造之路,目的是想让它在iOS7上同样支持IPv6。
改造方法认为为:
+ (instancetype)reachabilityForInternetConnection
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
}
修改的思路为:
- 获取设备IP地址
- 通过IP地址判断当前设备所处的网络环境
- 如果是IPv6网络环境,则使用struct sockaddr_in6来构造zeroAddress。
+ (instancetype)reachabilityForInternetConnection
{
// IPv4
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// 获取IP地址
NSString *ipStr = [self getIPAddress];
NSLog(@"ip address:%@",ipStr);
if ([ipStr rangeOfString:@"."].location == NSNotFound) {
// IPv6
struct sockaddr_in6 zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin6_len = sizeof(zeroAddress);
zeroAddress.sin6_family = AF_INET6;
return [self reachabilityWithAddress:(const struct sockaddr *)&zeroAddress];
}
return [self reachabilityWithAddress:(const struct sockaddr *)&zeroAddress];
}
+ (NSString *)getIPAddress {
NSString *address = @"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = getifaddrs(&interfaces); // retrieve the current interfaces - returns 0 on success
if (success == 0) {
// Loop through linked list of interfaces
temp_addr = interfaces;
while (temp_addr != NULL) {
// Check if interface is en0 which is the wifi connection on the iPhone
if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"] || [[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"pdp_ip0"])
{
// IPV4
if (temp_addr->ifa_addr->sa_family == AF_INET){
// Get NSString from C String
address = [self formatIPV4Address:((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr];
}
// IPV6
else if (temp_addr->ifa_addr->sa_family == AF_INET6){
address = [self formatIPV6Address:((struct sockaddr_in6 *)temp_addr->ifa_addr)->sin6_addr];
if (address && ![address isEqualToString:@""] && ![address.uppercaseString hasPrefix:@"FE80"]) break;
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}
//for IPV6
+ (NSString *)formatIPV6Address:(struct in6_addr)ipv6Addr{
NSString *address = nil;
char dstStr[INET6_ADDRSTRLEN];
char srcStr[INET6_ADDRSTRLEN];
memcpy(srcStr, &ipv6Addr, sizeof(struct in6_addr));
if(inet_ntop(AF_INET6, srcStr, dstStr, INET6_ADDRSTRLEN) != NULL){
address = [NSString stringWithUTF8String:dstStr];
}
return address;
}
//for IPV4
+ (NSString *)formatIPV4Address:(struct in_addr)ipv4Addr{
NSString *address = nil;
char dstStr[INET_ADDRSTRLEN];
char srcStr[INET_ADDRSTRLEN];
memcpy(srcStr, &ipv4Addr, sizeof(struct in_addr));
if(inet_ntop(AF_INET, srcStr, dstStr, INET_ADDRSTRLEN) != NULL){
address = [NSString stringWithUTF8String:dstStr];
}
return address;
}
修改好之后再次测试(iOS7系统,IPv6环境):
大功告成。
总结:如果你的项目也需要适配iOS7.0,那么这个博客可能有些许帮助,有这个要求的客户应该少数。如代码有问题,希望给予指点。