一.Ios过审的Ipv6问题
IOS官方返回的的报文中包含IPv6的文字,说明是App不兼容Ipv6网络地址引起的,确保App在Ipv6的环境下能够正常运行。
二.什么是Ipv6
IPV6,是对IPV4地址空间的扩充。目前当我们用iOS设备连接上Wifi、4G、3G等网络时,设备被分配的地址均是IPV4地址,但是随着运营商和企业逐渐部署IPV6 DNS64/NAT64网络之后,设备被分配的地址会变成IPV6的地址,而这些网络就是所谓的IPV6-Only网络,并且仍然可以通过此网络去获取IPV4地址提供的内容。客户端向服务器端请求域名解析,首先通过DNS64 Server查询IPv6的地址,如果查询不到,再向DNS Server查询IPv4地址,通过DNS64 Server合成一个IPV6的地址,最终将一个IPV6的地址返回给客户端。
三.如何解决
苹果审核Ipv6的标准是确保应用在Ipv6的环境下能够正常运行
Ipv6网络下的客户端是不能连接Ipv4的服务端的,需要通过DNS64或者NAT64的转换地址
DNS64/NAT64是苹果提供的转换通道,不需要开发者去考虑这个问题 ,只需要在发送连接请求时,根据当前网络环境去转换实际的连接地址,例如Ipv6,需要通过调用IOS官方提供的转换通道请求实际的连接地址
四.编写Ios地址转换的接口
新建两个记事本,并把名称带后缀分别修改成ipv6.h和ipv6.m,ipv6为名称(可以自定义)
编辑ipv6.h,输入以下代码
@interface BundleId : NSObject +(NSString *)getIPv6 : (const char *)mHost :(const char *)mPort; @end
编辑ipv6.m,输入以下代码
#import "BundleId.h" #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> #include <err.h> #define MakeStringCopy( _x_ ) ( _x_ != NULL && [_x_ isKindOfClass:[NSString class]] ) ? strdup( [_x_ UTF8String] ) : NULL const char* getIPv6(const char *mHost,const char *mPort) { if( nil == mHost ) return NULL; const char *newChar = "No"; const char *cause = NULL; struct addrinfo* res0; struct addrinfo hints; struct addrinfo* res; int n, s; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_DEFAULT; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if((n=getaddrinfo(mHost, "http", &hints, &res0))!=0) { printf("getaddrinfo error: %s\n",gai_strerror(n)); return NULL; } struct sockaddr_in6* addr6; struct sockaddr_in* addr; NSString * NewStr = NULL; char ipbuf[32]; s = -1; for(res = res0; res; res = res->ai_next) { if (res->ai_family == AF_INET6) { addr6 =( struct sockaddr_in6*)res->ai_addr; newChar = inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, sizeof(ipbuf)); NSString * TempA = [[NSString alloc] initWithCString:(const char*)newChar encoding:NSASCIIStringEncoding]; NSString * TempB = [NSString stringWithUTF8String:"&&ipv6"]; NewStr = [TempA stringByAppendingString: TempB]; printf("%s\n", newChar); } else { addr =( struct sockaddr_in*)res->ai_addr; newChar = inet_ntop(AF_INET, &addr->sin_addr, ipbuf, sizeof(ipbuf)); NSString * TempA = [[NSString alloc] initWithCString:(const char*)newChar encoding:NSASCIIStringEncoding]; NSString * TempB = [NSString stringWithUTF8String:"&&ipv4"]; NewStr = [TempA stringByAppendingString: TempB]; printf("%s\n", newChar); } break; } freeaddrinfo(res0); printf("getaddrinfo OK"); NSString * mIPaddr = NewStr; return MakeStringCopy(mIPaddr); }
保存ipv6.m和ipv6.h,并把它们放在Unity的Plugins/IOS的目录下
五.结合编辑的工具,解决Ipv6问题
编辑DNSParse.cs,输入以下代码,调用上一步编辑的接口
//调用工具的接口 #if UNITY_IPHONE [DllImport("__Internal")] private static extern string getIPv6(string mHost, string mPort); #endif /// <summary> /// 获取Ipv6地址,地址格式为"192.168.1.1&&ipv4"用于解析地址 /// </summary> /// <param name="mHost">域名</param> /// <param name="mPort">端口</param> /// <returns></returns> public static string GetIPv6(string mHost, string mPort) { #if UNITY_IPHONE && !UNITY_EDITOR string mIPv6 = getIPv6(mHost, mPort); return mIPv6; #else return mHost + "&&ipv4"; #endif } /// <summary> /// 解析地址 /// </summary> /// <param name="serverIp">域名</param> /// <param name="serverPorts">端口</param> /// <param name="newServerIp">输出新的地址</param> /// <param name="mIPType">网络类型</param> public static void PareseIP(string serverIp, string serverPorts, out string newServerIp, out AddressFamily mIPType) { mIPType = AddressFamily.InterNetwork; newServerIp = serverIp; try { string mIPv6 = GetIPv6(serverIp, serverPorts); if (!string.IsNullOrEmpty(mIPv6)) { string[] m_StrTemp = System.Text.RegularExpressions.Regex.Split(mIPv6, "&&"); if (m_StrTemp != null && m_StrTemp.Length >= 2) { string IPType = m_StrTemp[1]; if (IPType == "ipv6") { newServerIp = m_StrTemp[0]; mIPType = AddressFamily.InterNetworkV6; } } } } catch (Exception e) { Debug.Log("解析出错,错误类型:"+e.Message); } }
查询NetworkInterface.cs脚本(Kbengine客户端导出包中的类),定位到connectTo的方法,修改如下
public void connectTo(string ip, int port, ConnectCallback callback, object userData) { if (valid()) throw new InvalidOperationException("Have already connected!"); string newServerIp = ""; AddressFamily newAddressFamily = AddressFamily.InterNetwork; //解析是否是Ipv6地址,如果是返回Ipv6地址,如果不是保持原来地址不变 DNSParase.PareseIP(ip, port.ToString(), out newServerIp, out newAddressFamily); if (!string.IsNullOrEmpty(newServerIp)) { ip = newServerIp; } Dbg.DEBUG_MSG("获取的Ip:"+ip+" 协议:"+newAddressFamily); _socket = new Socket(newAddressFamily, SocketType.Stream, ProtocolType.Tcp); _socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, KBEngineApp.app.getInitArgs().getRecvBufferSize() * 2); _socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, SocketOptionName.SendBuffer, KBEngineApp.app.getInitArgs().getSendBufferSize() * 2); _socket.NoDelay = true; //_socket.Blocking = false; ConnectState state = new ConnectState(); state.connectIP = ip; state.connectPort = port; state.connectCB = callback; state.userData = userData; state.socket = _socket; state.networkInterface = this; Dbg.DEBUG_MSG("connect to " + ip + ":" + port + " ..."); connected = false; // 先注册一个事件回调,该事件在当前线程触发 Event.registerIn("_onConnectionState", this, "_onConnectionState"); var v = new AsyncConnectMethod(this._asyncConnect); v.BeginInvoke(state, new AsyncCallback(this._asyncConnectCB), state); }
注意事项,网络通信使用域名连接
测试环境搭建相关文章 https://blog.csdn.net/iosworker/article/details/51595432