苹果公司6月1日后发布的应用必须支持IPv6-Only网络的解决办法(底层socket连接的IPv6支持方案)

来源:互联网 发布:hr绿宝瓶面霜 知乎 编辑:程序博客网 时间:2024/06/01 08:12

苹果在去年的WWDC2015中宣布在今年的6月1日之后发布的应用必须支持IPv6-Only的网络环境。

苹果官方给出的解决方法是:

  • IP address literals embedded in protocols. Many communications protocols, such as Session Initiation Protocol (SIP), File Transfer Protocol (FTP), WebSockets, and Peer-to-Peer Protocol (P2PP), include IP address literals in protocol messages. For example, the FTP parameter commands DATA PORTand PASSIVE exchange information that includes IP address literals. Similarly, IP address literals may appear in the values of SIP header fields, such as ToFromContactRecord-Route, and Via. See Use High-Level Networking Frameworks and Don’t Use IP Address Literals.

  • IP address literals embedded in configuration files. Configuration files often include IP address literals. See Don’t Use IP Address Literals.

  • Network preflighting. Many apps attempt to proactively check for an Internet connection or an active Wi-Fi connection by passing IP address literals to network reachability APIs. See Connect Without Preflight.

  • Using low-level networking APIs. Some apps work directly with sockets and other raw network APIs such as gethostbynamegethostbyname2, and inet_aton. These APIs are prone to misuse or they only support IPv4—for example, resolving hostnames for the AF_INET address family, rather than the AF_UNSPEC address family. See Use High-Level Networking Frameworks.

  • Using small address family storage containers. Some apps and networking libraries use address storage containers—such as uint32_tin_addr, and sockaddr_in—that are 32 bits or smaller. See Use Appropriately Sized Storage Containers. [1]

而在我们的工程中需要使用底层C++的socket建立长连接来进行信息交互,因此我们需要对我们的工程中的socket连接进行IPv6 Only网络下的适配工作。

首先介绍下IPv6:

从很久远的计算机网络课堂中我们得知IPv6是为了弥补IPv4网络环境下地址数量有限而诞生的容量更大的网络地址协议。目前在中国,当我们用网络设备连接上有线网络、大部分无线网络(部分无线路由局域网内使用IPv6)、4G、3G等网络时,设备被分配的地址均为IPv4地址(教育网部分使用IPv6),但是随着不同国家不同的运营商和企业逐渐部署IPv6 DNS64/NAT64网络之后,设备被分配的地址会变成IPv6的地址,而这些网络就是所谓的IPV6-Only网络,并且仍然可以通过此网络去获取IPV4地址提供的内容 [3]。

他们访问IPv4地址时的过程是这样的:

客户端向服务器端请求域名解析,首先通过DNS64 Server查询IPv6的地址,如果查询不到,再向DNS Server查询IPv4地址,通过DNS64 Server将查询到的IPv4地址合成为一个IPv6的地址,最终将一个IPv6的地址返回给客户端 [3]。如下图所示:

而在我们的底层socket连接中,并不支持使用域名来通过DNS Server来兼容IPv4和IPv6网络来建立连接,而需要我们以P2P的方式直接使用服务器的IP Address来直接建立连接。那么我们需要在IPv6-Only的网络下手动把IPv4的地址合成为IPv6的地址,来访问服务器。此外对于服务端我们需要的让服务器能够兼容IPv4和IPv6的客服端发来的连接请求,我不太清楚服务端的代码,具体方法请参考参考文献[2]。

在代码中我们建立socket连接的地方,我们需要手动来判断当前网络来生成对应IP格式。这里在iOS 9.2版本之后,苹果宣布 'getaddrinfo' 方法将可以支持按照当前网络环境得到IPv4或者IPv6的地址。这需要我们载入"#include <netdb.h>"和"#include <arpa/inet.h>"。具体的代码如下:

// clear previous socketsif(mySocket>=0){    close(mySocket);    mySocket=-1;}struct addrinfo hints, *res, *ressave;bzero(&hints, sizeof(hints));hints.ai_family = PF_UNSPEC;hints.ai_socktype = SOCK_STREAM;char szPort[10]={0};sprintf(szPort, "%d",nPort);int ret;// get IPv4 or IPv6 address according to the App running network (IPv4 or IPv6)if (0 != (ret = getaddrinfo(szIP, szPort, &hints, &res))){    return -1;}// set the port of IP address, because sometime the port value will be set to '0'if (res->ai_family == AF_INET6) {{    sockaddr_in6 *paddrinfo=(sockaddr_in6 *)res->ai_addr;    paddrinfo->sin6_port = htons(nPort);} else if (res->ai_family == AF_INET) {    sockaddr_in6 *paddrinfo=(sockaddr_in *)res->ai_addr;    paddrinfo->sin6_port = htons(nPort);}ressave = res;while (NULL != res){    //init socket    if (-1 == (mySocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))    {                res = res->ai_next;        continue;    }    //connect to the server    if (-1 == connect(mySocket, res->ai_addr, res->ai_addrlen))    {        close(mySocket);        mySocket=-1;        res = res->ai_next;        continue;    }    break;}freeaddrinfo(ressave);
建立完socket连接后,就可以按照原先的网络进行正常的发送/接收数据了。


相关参考文献:

1.Supporting IPv6 DNS64/NAT64 Networks

2.Application Aspects of IPv6 Transition

3.iOS应用支持IPV6,就那点事儿

4.针对苹果最新审核要求为应用兼容IPv6

5.NAT64与DNS64基本原理概述






0 0