支持苹果IPV6 ONLY 的socket 修改方法

首先别错误理解以为app 必须支持 ipv6 的服务端, 只需要支持 ipv6的客户端需要访问 ipv4 的服务端。

目前需要经过  NAT64 转换就能达到目的,客户端只需要支持该转换就能实现, 在OS X的系统上是的wifi 共享有支持 NAT64转换,提供了这个功能就方便了我们可以使用手机链接 OS X上的热点模拟这个测试环境。

我们代码上所需要完成​的事情:

1.客户端需要链接的ip地址(ipv4) 通过转换。

 1 getaddrinfo​ //方法可以转换为 ipv6 的地址。
 2 
 3struct addrinfo hints, *res, *res0;
 4 
 5 memset(&hints, 0, sizeof(hints));
 6 
 7 hints.ai_family = PF_UNSPEC;
 8 
 9 hints.ai_socktype = SOCK_STREAM;
10 
11 #if (PLATFORM == PLATFORM_WIN32)
12 
13 hints.ai_flags = AI_PASSIVE;
14 
15 #else
16 
17 hints.ai_flags = AI_DEFAULT;
18 #endif

19 int error = getaddrinfo(pServerIP, "http", &hints, &res0);

2.经过转换后我们可以判断 ai_family 兼容 非NAT64 转换的网络环境与 NAT64转换的网络环境 下进行 socket的链接:

 1 struct sockaddr_in addr_in;
 2 
 3 struct sockaddr_in6 addr_in6;
 4 
 5 int connect_ret;
 6 
 7 for (res = res0; res; res = res->ai_next) {
 8 
 9      connect_ret = -1;
10 
11       tempSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
12 
13      if (tempSocket < 0) {
14 
15              continue;
16 
17        }
18 
19       if(AF_INET == res->ai_family){
20 
21            addr_in = *((struct sockaddr_in *)res->ai_addr);
22 
23            addr_in.sin_port = htons(ServerPort);
24 
25            connect_ret = connect(tempSocket, (struct sockaddr *)&addr_in, res-      >ai_addrlen);
26 
27         }else if(AF_INET6 == res->ai_family){
28 
29                 addr_in6  = *((struct sockaddr_in6 *)res->ai_addr);
30 
31                 addr_in6.sin6_port = htons(ServerPort);
32 
33                 connect_ret = connect(tempSocket, (struct sockaddr *)&addr_in6, res->ai_addrlen);
34 
35         }
36 
37        if (connect_ret < 0) {
38 
39             cause = "connect";
40 
41 #if (PLATFORM == PLATFORM_WIN32)
42 
43             closesocket(tempSocket);
44 
45 #else
46 
47             close(tempSocket);
48 
49 #endif
50 
51             tempSocket = -1;
52 
53             continue;
54 
55       }
56 
57       break; 
58 
59 }

OK,这样就支持 ipv6 对 ipv4 的网络 而且兼容 普通 ipv4 对 ipv4 的网络。​

 

上面是没有考虑链接时候客户端会进入卡死状态直到链接成功, 因为加上去代码会复杂点,为了好看就没有加上, 真实项目中需要改为非阻塞模式, 通过select 来判断是否connect成功。

下面是加上非阻塞模式:

 1     int tempSocket = -1;
 2     struct addrinfo hints, *res, *res0;
 3     int error;
 4     const char *cause = NULL; 
 5 
 6     memset(&hints, 0, sizeof(hints));
 7     hints.ai_family = PF_UNSPEC;
 8     hints.ai_socktype = SOCK_STREAM;
 9 
10     error = getaddrinfo(pServerIP, NULL, &hints, &res0);
11     if (error) {
12         CCLOG("getaddrinfo error:%d", error);
13         return false;
14     }
15 
16 
17     struct sockaddr_in addr_in;
18     struct sockaddr_in6 addr_in6;
19     for (res = res0; res; res = res->ai_next) {
20         tempSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
21         if (tempSocket < 0) {
22             cause = "socket";
23             continue;
24         }
25 
26 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
27         // 设定 非阻塞
28         unsigned long tmepOption = 1;  //0:阻塞
29         ioctlsocket(tempSocket, FIONBIO, &tmepOption);
30 #else
31         // 设定 非阻塞
32         int flag = fcntl(tempSocket, F_GETFL, 0);
33         fcntl(tempSocket, F_SETFL, flag | O_NONBLOCK);
34 #endif
35 
36         if(AF_INET == res->ai_family){
37             addr_in = *((struct sockaddr_in *)res->ai_addr);
38             addr_in.sin_port = htons(ServerPort);
39             connect(tempSocket, (struct sockaddr *)&addr_in, res->ai_addrlen);
40         }else if(AF_INET6 == res->ai_family){
41             addr_in6 = *((struct sockaddr_in6 *)res->ai_addr);
42             addr_in6.sin6_port = htons(ServerPort);
43             connect(tempSocket, (struct sockaddr *)&addr_in6, res->ai_addrlen);
44         }
45 
46         fd_set fset;  
47         FD_ZERO(&fset); 
48         FD_SET(tempSocket, &fset);
49 
50         timeval timeout;
51         timeout.tv_sec  = 1;  // 1.5秒超时, 上层逻辑会尝试3次 >> 1.5 * 3
52         timeout.tv_usec = 500; 
53 #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
54         socklen_t len = sizeof(error);
55 #else
56         int len = sizeof(error);
57 #endif
58         if( select(tempSocket+1, NULL, &fset, NULL, &timeout) > 0)  
59         {  
60             if (FD_ISSET(tempSocket, &fset))
61             {
62                 getsockopt(tempSocket, SOL_SOCKET, SO_ERROR, (char *)&error, &len);  
63                 if(error == 0){
64                     break;
65                 }
66             }
67         }
68 
69 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
70         closesocket(tempSocket);
71 #else
72         close(tempSocket);
73 #endif
74         tempSocket = -1; 
75 
76     }
77 
78     if (tempSocket < 0) {
79         CCLOG("connect server error:%d", tempSocket);
80         goto RETURN_FALSE;
81     }
82 
83 
84     if(false){
85 RETURN_FALSE:
86         freeaddrinfo(res0);
87         return false;
88     }

 

转载于:https://www.cnblogs.com/CodeerHome/p/5646168.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值