iOS应用如何支持IPV6
本文轉自 http://www.code4app.com/forum.php?mod=viewthread&tid=8427&highlight=ipv6
果然是蘋果打個哈欠,iOS行業內就得起一次風暴呀。自從5月初Apple明文規定所有開發者在6月1號以后提交新版本需要支持IPV6-Only的網絡,大家便開始熱火朝天的研究如何支持IPV6,以及應用中哪些模塊目前不支持IPV6。一、IPV6-Only支持是啥?
首先IPV6,是對IPV4地址空間的擴充。目前當我們用iOS設備連接上Wifi、4G、3G等網絡時,設備被分配的地址均是IPV4地址,但是隨著運營商和企業逐漸部署IPV6 DNS64/NAT64網絡之后,設備被分配的地址會變成IPV6的地址,而這些網絡就是所謂的IPV6-Only網絡,并且仍然可以通過此網絡去獲取IPV4地址提供的內容。客戶端向服務器端請求域名解析,首先通過DNS64 Server查詢IPv6的地址,如果查詢不到,再向DNS Server查詢IPv4地址,通過DNS64 Server合成一個IPV6的地址,最終將一個IPV6的地址返回給客戶端。如圖所示: <ignore_js_op>?NAT64-DNS64-ResolutionOfIPv4_2x.png
local_ipv6_dns64_nat64_network_2x.png
- 參考資料:
- https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW1
二、Apple如何審核支持IPV6-Only?
三、應用如何支持IPV6-Only?
1. Use High-Level Networking Frameworks;2. Don’t Use IP Address Literals;3. Check Source Code for IPv6 DNS64/NAT64 Incompatibilities;4. Use System APIs to Synthesize IPv6 Addresses;3.1 NSURLConnection是否支持IPV6?
官方的這句話讓我們疑惑頓生:using high-level networking APIs such as NSURLSession and the CFNetwork frameworks and you connect by name, you should not need to change anything for your app to work with IPv6 addresses 只說了NSURLSession和CFNetwork的API不需要改變,但是并沒有提及到NSURLConnection。 從上文的參考資料中,我們看到NSURLSession、NSURLConnection同屬于Cocoa的url loading system,可以猜測出NSURLConnection在ios9上是支持IPV6的。 應用里面的API網絡請求,大家一般都會選擇AFNetworking進行請求發送,由于歷史原因,應用的代碼基本上都深度引用了AFHTTPRequestOperation類,所以目前API網絡請求均需要通過NSURLConnection發送出去,所以必須確認NSURLConnection是否支持IPV6. 經過測試,NSURLConnection在最新的iOS9系統上是支持IPV6的。
3.2 Cocoa的URL Loading System從iOS哪個版本開始支持IPV6?
3.3 Reachability是否需要修改支持IPV6?
| 1 2 3 | 在Pods:Reachability中 AF_INET????????????????? Files:Reachability.m struct sockaddr_in?????? Files:Reachability.h , Reachability.m |
(1)目前Github的開源庫Reachability的最新版本是3.2,蘋果也出了一個Support IPV6 的Reachability的官方樣例,我們比較了一下源碼,跟Github上的Reachability沒有什么差異。
(2)我們通常都是通過一個0.0.0.0 (ZeroAddress)去開啟網絡狀態監控,經過我們測試,在iOS9以上的系統上IPV4和IPV6網絡環境均能夠正常使用;但是在iOS8上IPV4和IPV6相互切換的時候無法監控到網絡狀態的變化,可能是因為蘋果在iOS8上還并沒有對IPV6進行相關支持相關。(但是這仍然滿足蘋果要求在最新系統版本上支持IPV6的網絡)。
(3)當大家都在要求Reachability添加對于IPV6的支持,其實蘋果在iOS9以上對Zero Address進行了特別處理,官方發言是這樣的:
reachabilityForInternetConnection: This monitors the address 0.0.0.0,
which reachability treats as a special token that causes it to actually
monitor the general routing status of the device, both IPv4 and IPv6.
?
[Objective-C]?查看源文件?復制代碼 ?| 1 2 3 4 5 6 7 | + (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]; } |
四、底層的socket API如何同時支持IPV4和IPV6?
開源地址:https://github.com/Lede-Inc/LDNetDiagnoService_IOS.git
這個網絡診斷組件的主要功能如下:
- 本地網絡環境的監測(本機IP+本地網關+本地DNS+域名解析);
- 通過TCP Connect監測到域名的連通性;
- 通過Ping 監測到目標主機的連通耗時;
- 通過traceRoute監測設備到目標主機中間每一個路由器節點的ICMP耗時;
4.1 IP地址從二進制到符號的轉化
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //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; } |
4.2 本機IP獲取支持IPV6
(1)在模擬器和真機上都會出現以FE80開頭的IPV6單播地址影響我們判斷,所以在這里進行特殊的處理(當第一次遇到不是單播地址的IP地址即為本機IP地址)。
(2)在IPV6環境下,真機測試的時候,第一個出現的是一個IPV4地址,所以在IPV4條件下第一次遇到單播地址不退出。 [Objective-C]?查看源文件?復制代碼 ?
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | + (NSString *)deviceIPAdress { ????????while (temp_addr != NULL) { ????????????NSLog(@"ifa_name===%@",[NSString stringWithUTF8String:temp_addr->ifa_name]); ????????????// 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; ????????} ????} } |
4.3 設備網關地址獲取獲取支持IPV6
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* net.route.0.inet.flags.gateway */ ??int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY}; ??if (sysctl(mib, sizeof(mib) / sizeof(int), buf, &l, 0, 0) < 0) { ???????address = @"192.168.0.1"; ??} ??.... ??//for IPV4 ??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; ??????????????} ??????????} //for IPV6 ???for (i = 0; i < RTAX_MAX; i++) { ??????????????if (rt->rtm_addrs & (1 << i)) { ??????????????????sa_tab[i] = sa; ??????????????????sa = (struct sockaddr_in6 *)((char *)sa + sa->sin6_len); ??????????????} else { ??????????????????sa_tab[i] = NULL; ??????????????} ??????????} |
4.4 設備DNS地址獲取支持IPV6
?
[Objective-C]?查看源文件?復制代碼 ?| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | +(NSArray *)outPutDNSServers{ ????res_state res = malloc(sizeof(struct __res_state)); ????int result = res_ninit(res); ????NSMutableArray *servers = [[NSMutableArray alloc] init]; ????if (result == 0) { ????????union res_9_sockaddr_union *addr_union = malloc(res->nscount * sizeof(union res_9_sockaddr_union)); ????????res_getservers(res, addr_union, res->nscount); ????????for (int i = 0; i < res->nscount; i++) { ????????????if (addr_union[i].sin.sin_family == AF_INET) { ????????????????char ip[INET_ADDRSTRLEN]; ????????????????inet_ntop(AF_INET, &(addr_union[i].sin.sin_addr), ip, INET_ADDRSTRLEN); ????????????????NSString *dnsIP = [NSString stringWithUTF8String:ip]; ????????????????[servers addObject:dnsIP]; ????????????????NSLog(@"IPv4 DNS IP: %@", dnsIP); ????????????} else if (addr_union[i].sin6.sin6_family == AF_INET6) { ????????????????char ip[INET6_ADDRSTRLEN]; ????????????????inet_ntop(AF_INET6, &(addr_union[i].sin6.sin6_addr), ip, INET6_ADDRSTRLEN); ????????????????NSString *dnsIP = [NSString stringWithUTF8String:ip]; ????????????????[servers addObject:dnsIP]; ????????????????NSLog(@"IPv6 DNS IP: %@", dnsIP); ????????????} else { ????????????????NSLog(@"Undefined family."); ????????????} ????????} ????} ????res_nclose(res); ????free(res); ????return [NSArray arrayWithArray:servers]; } |
4.4 域名DNS地址獲取支持IPV6
| 1 2 3 4 5 | //ipv4 phot = gethostbyname(hostN); //ipv6 ?phot = gethostbyname2(hostN, AF_INET6); |
4.5 ping方案支持IPV6
https://developer.apple.com/library/mac/samplecode/SimplePing/Introduction/Intro.html 只是需要注意的是:
(1)返回的packet去掉了IPHeader部分,IPV6的header部分也不返回TTL(Time to Live)字段;
(2)IPV6的ICMP報文不進行checkSum的處理;
4.6 traceRoute方案支持IPV6
兩個關鍵的地方需要注意:
(1)IPV6中去掉IP_TTL字段,改用跳數IPV6_UNICAST_HOPS來表示;
(2)sendto方法可以兼容支持IPV4和IPV6,但是需要最后一個參數,制定目標IP地址的大小;因為前一個參數只是指明了IP地址的開始地址。千萬不要用統一的sizeof(struct sockaddr), 因為sockaddr_in 和 sockaddr都是16個字節,兩者可以通用,但是sockaddr_in6的數據結構是28個字節,如果不顯式指定,sendto方法就會一直返回-1,erroNo報22 Invalid argument的錯誤。
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //構造通用的IP地址結構stuck sockaddr ?NSString *ipAddr0 = [serverDNSs objectAtIndex:0]; ????//設置server主機的套接口地址 ????NSData *addrData = nil; ????BOOL isIPV6 = NO; ????if ([ipAddr0 rangeOfString:@":"].location == NSNotFound) { ????????isIPV6 = NO; ????????struct sockaddr_in nativeAddr4; ????????memset(&nativeAddr4, 0, sizeof(nativeAddr4)); ????????nativeAddr4.sin_len = sizeof(nativeAddr4); ????????nativeAddr4.sin_family = AF_INET; ????????nativeAddr4.sin_port = htons(udpPort); ????????inet_pton(AF_INET, ipAddr0.UTF8String, &nativeAddr4.sin_addr.s_addr); ????????addrData = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; ????} else { ????????isIPV6 = YES; ????????struct sockaddr_in6 nativeAddr6; ????????memset(&nativeAddr6, 0, sizeof(nativeAddr6)); ????????nativeAddr6.sin6_len = sizeof(nativeAddr6); ????????nativeAddr6.sin6_family = AF_INET6; ????????nativeAddr6.sin6_port = htons(udpPort); ????????inet_pton(AF_INET6, ipAddr0.UTF8String, &nativeAddr6.sin6_addr); ????????addrData = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; ????} ????struct sockaddr *destination; ????destination = (struct sockaddr *)[addrData bytes]; //創建socket if ((recv_sock = socket(destination->sa_family, SOCK_DGRAM, isIPV6?IPPROTO_ICMPV6:IPPROTO_ICMP)) < 0) if ((send_sock = socket(destination->sa_family, SOCK_DGRAM, 0)) < 0) //設置sender 套接字的ttl if ((isIPV6? setsockopt(send_sock,IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)): setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) < 0) //發送成功返回值等于發送消息的長度 ssize_t sentLen = sendto(send_sock, cmsg, sizeof(cmsg), 0, (struct sockaddr *)destination, isIPV6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)); |
轉載于:https://www.cnblogs.com/idxdm/p/5553423.html
總結
以上是生活随笔為你收集整理的iOS应用如何支持IPV6的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: supervisor开机自启动方法
- 下一篇: R作图-----北京市2017年一季度A