导语:
自从5月初Apple明文规定所有开发者在6月1号以后提交新版本需要支持IPV6-Only的网络。自从苹果实施以来,也遇到了各种各样的问题,现此把知道的资料整理一下
首先,说明一下什么是IPv6
IPv6是Internet Protocol Version 6的缩写,其中Internet Protocol译为“互联网协议”。IPv6是IETF(互联网工程任务组,Internet Engineering Task Force)设计的用于替代现行版本IP协议(IPv4)的下一代IP协议,号称可以为全世界的每一粒沙子编上一个网址[1]。
为什么用IPv6?
由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍。
与IPv4相比,IPv6具有更大的地址空间。IPv4中规定IP地址长度为32,最大地址个数为232;而IPv6中IP地址的长度为128,即最大地址个数为2128。与32位地址空间相比,其地址空间增加了2128-232个。
其他优势,请大家自行Google。
IPv6地址是什么样的?
IPv6地址格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
苹果指的IPv6-Only网络是什么?
IPv6 DNS64/NAT64网络即是所谓的IPV6-Only网络,该网络可以通过转换的方式来继续访问IPv4地址。
IPv6 DNS64/NAT64网络是什么?
可以通过以下流程图和时序图来了解
其中
DNS(Domain Name System,域名系统),通过DNS服务器,解析域名为IP地址
NAT(Network Address Translation,网络地址转换),内网IP地址到外网IP地址的转换
我现在是IPv4网络环境,我该怎么测试IPv6网络呢?
苹果提供了一个搭建虚拟IPv6 DNS64/NAT64网络的方法,要求Mac设备在Mac OS 10.11以上系统,通过有线网开一个IPv6-Only网络的热点,原理图如下:
步骤如下:
- 进入设置页面,按住Option键点击分享
- 勾选创建NAT64网络
- 点击互联网共享,共享以下来源的链接,选择以太网;用以下端口共享给电脑,选择Wifi
- 点击Wifi选项,设置Wifi名称和密码
- 勾选互联网共享,之后一直点确定即可
具体操作图如下:
出现以下图片,即热点开成功了
Apple如何审核支持IPV6-Only?
经过查阅相关资料和上传的版本来看,苹果审核的时候使用最新的系统(目前是IOS9.3.2)来测试IPv6-Only网络的,应该是在该网络下可以正常使用即可。并不会扫描应用内IPv4专有的底层API。
Note: In iOS 9 and OS X 10.11 and later, NSURLSession and CFNetwork automatically synthesize IPv6 addresses from IPv4 literals locally on devices operating on DNS64/NAT64 networks. However, you should still work to rid your code of IP address literals.
对于使用了写死IPv4地址来交互的朋友。苹果提到,在iOS9以上的系统,苹果自己会做IPv4转IPv6地址的转换,所以如果应用中写死的IPv4地址,并且是使用NSURLSession and CFNetwork(测试发现NSURLConnection也是可以的)来做网络请求,则在IOS9以上系统,还是可以在IPv6-Only网络正常运行的,所以是可以通过审核的之后可能还是有风险,建议还是换成域名)。
应用如何支持IPV6-Only?
- 使用AFetworking做网络交互的朋友,需要使用最新版本替换AFNetworking中的Reachability类(看源码发现,也只是针对iOS9以上的系统做了特殊处理)
- ASIHttpRequest虽然使用的是CFNetwork,但是在网上看到有人反应,IPv6网络上不能正常运行,所以。。。还是换AFNetworking吧,ASI早就没人维护了
- 对于底层的socket API,这一块,主要是在IPv6-Only网络下,需要将IPv4地址转换为IPv6地址,可以参考笔者后面提供的代码,还要要注意的是,可能有的服务端那边会通过请求头中的Host来校验是IPv4还是IPv6环境(不处理的话,默认Host中会是IPv6地址),所以Host这个地方需要注意一下(服务端没有配IPv6地址的换,可能会有问题),根据具体场景来处理
- 对于播放这一块,如果使用的是系统的播放器,走的是HLS,而且使用的写死IPv4地址。对于清流的片源,经过我们的测试,只要把开始的播放URL(重定向之前的URL)中将IPv4地址转换为IPv6地址即可。
iOS中将IPv4地址转换为IPv6地址
在iOS 9.2版本系统后,通过getaddrinfo转换ipv4得到ipv6地址,该方法只能在iOS9.2以后才有效。
OC代码
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
#define CopyString(temp) (temp != NULL)? strdup(temp):NULL
- (NSString *)getIPAddress:(NSString *)ipAddress {
if (!ipAddress) {
return @"";
}
struct addrinfo hints, *res, *res0;
int error;
const char *ipv4_str = [ipAddress UTF8String];
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(ipv4_str, "http", &hints, &res0);
if (error) {
errx(1, "%s", gai_strerror(error));
return @"";
}
struct sockaddr_in6* addr6;
struct sockaddr_in * addr;
const char* pszTemp;
for (res = res0; res; res = res->ai_next) {
char buf[32];
if(res->ai_family == AF_INET6) {
addr6 = (struct sockaddr_in6*)res->ai_addr;
pszTemp = inet_ntop(AF_INET6, &addr6->sin6_addr, buf, sizeof(buf));
} else {
addr = (struct sockaddr_in*)res->ai_addr;
pszTemp = inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf));
}
break;
}
freeaddrinfo(res0);
printf("getaddrinfo ok %s\n", pszTemp);
return [NSString stringWithFormat:@"%s",CopyString(pszTemp)];
}
C++代码可以参照:
iOS 解决ipv6问题