1、前序
首先,众所周知的原因,苹果从很早开始都要求开发者必须支持IPV6,刚开始由于更重原因,改造还是很慢,无论是作为开发者的我们还是运营商,但是随着时间的推进,企业和移动通信供应商在逐步部署IPv6 DNS64/NAT64网络。IPv6DNS64/NAT64是一个仅有IPv6的网络,且能通过转换继续支持IPv4。同时4G的普及,由于IPv4地址的限制,为了保证4G开发的扩展性,需要IPv6的支持。当然目前国内的三大运营商,他们的IPV6的发展规模现在各不相同。
通过这里就了解到了一些前提。这边再说下DNS64/NAT64转换流程
DNS64/NAT64转换流程
参考Apple官方文档 Supporting IPv6 DNS64/NAT64 Networks


如果客户端向DNS64服务器发起一个DNS查询,当DNS找到一个基于IPv6的地址后,立刻返回客户端。如果无法找到对应的IPv6地址,DNS64服务器将请求IPv4地址,然后DNS64服务器将IPv4作为前缀合成一个IPv6地址,并且将其返回给客户端。这样,客户端将总是获得一个IPv6目标地址.详细流程见下图

而对于客户端如果本地网络同时提供IPv4和IPv6的环境时(称为双栈),访问IPv4服务器就直接创建IPv4 socket进行链接。
如果本地网络只提供了IPv6环境(称为IPv6-Only),访问IPv4服务器时就先转成IPv6地址,网关收到后再由NAT64服务解析成IPv4,走后面的IPv4网络。
2.由bug产生的思考🤔
bug:有客户反应,在4G状态下,上传图片会超时失败,但是在WiFi下没有这个问题。
非常奇怪的bug,因为经过我不停的复现就是复现不了,但是客户的客户端确实必现的,在尝试努力了几次后,险些放弃查找原因,认为是正常的网络不稳定的错误,但是在后期,陆续又有客户反馈有类似问题,而且都是在4G的网络下。通过本地日志查看,调试,大致猜测问题可能和IPV6网路有关。
3.突破口
真的是运气,在某个阴郁天气的日子,我又通过自己的手机尝试了下问题,问题复现,而且是必现,公司其他同事的手机都没问题,只有我的手机问题复现,哈哈哈哈,立刻打开电脑,开启xcode进行调试操作,发现问题所在是在底层的socket连接库的问题。灵机一动,把sim卡放入其他人的手机尝试,问题复现。。。看来问题和运营商也有关系。
问题定位到socket IPV6连接error错误。尝试验证自己的想法。 验证手机的网络地址
问题复现的sim卡:

正常没有该问题的sim卡:

确实问题和IPV6网路有关。证实。
4.bug排故
通过调试发现,在判断网络时,该sim卡网络被判断为IPV6-only,但是实际上是IPV^/IPV4双栈的网络。发现原来是判断网络的方法实现有问题
int32 getdefaultgateway(in_addr* addr) 判断是否支持ipv4
百度或者谷歌能搜到很多相关实现 例如:
int getdefaultgateway(in_addr_t * addr)
{
int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
NET_RT_FLAGS, RTF_GATEWAY};
size_t l;
char * buf, * p;
struct rt_msghdr * rt;
struct sockaddr * sa;
struct sockaddr * sa_tab[RTAX_MAX];
int i;
int r = -1;
if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
return -1;
}
if(l>0) {
buf = malloc(l);
if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
return -1;
}
for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
rt = (struct rt_msghdr *)p;
sa = (struct sockaddr *)(rt + 1);
for(i=0; i<RTAX_MAX; i++) {
if(rt->rtm_addrs & (1 << i)) {
sa_tab[i] = sa;
sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
} else {
sa_tab[i] = NULL;
}
}
if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
&& sa_tab[RTAX_DST]->sa_family == AF_INET
&& sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
char ifName[128];
if_indextoname(rt->rtm_index,ifName);
if(strcmp("en0",ifName)==0){
*addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
r = 0;
}
}
}
}
free(buf);
}
return r;
}
为什么明明支持IPV6但是判断为IPV4呢。深入方法调试查看

关键就是en0的判断,en0表示的是wifi,也就是说只有在wifi状态下才能进入if逻辑中,4G状态下是进入不了判断语句的。
尝试把此判断去除,问题解决。。。 问题排除
此处可以进行兜底逻辑,如果是IPV^-only的地址,进行是否可以合成IPV6的判断。如果运营商支持IPV6-only就一定有IPV4合成IPV6的能力,用于支持IPV4。如果是双栈就不用去合成IPV6地址,可以直接走IPV4地址。