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/NAT64的轉換流程。這是個純IPv6網路,並通過轉換也可繼續訪問IPv4的內容。這樣能保證所有的網路都能訪問。上圖: 就是目前運營商的一個標準解析流程如果客戶端向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地址。