原因
- 苹果突然宣布2016.6.1之后提交的应用必须通过ipv6审核
- 我们的项目中大量使用的socket tcpclient等api
环境搭建
- 找一个mac
- 偏好设置中按住
command键 点开共享 - 分享wifi 勾上 nat64
- 手机连上这个wifi即可
解决思路
- 需要判断是否为 ipv6-only 的环境
- 通过dns api 即可得到返回
- 需要将现有的ipv4地址转换为ipv6
- ipv6的socket必须使用ipv6的地址
- ipv4转ipv6的转换机制很多,苹果希望的是 NAT64/DNS64 即 64:ff9b::/96
代码
代码不多直接贴上来了
public class SocketHelper
{
private static bool? mIsIpv6Only;
/// <summary>
/// 判断是否为Ipv6-only环境
/// Dns.GetHostAddresses 为耗时操作
/// </summary>
public static bool IsIpv6Only
{
get
{
#if UNITY_IPHONE
if (mIsIpv6Only.HasValue)
return mIsIpv6Only.Value;
else
{
IPAddress[] addrs = null;
try
{
addrs = Dns.GetHostAddresses("www.qq.com");
}
catch(Exception ex)
{
Debug.LogError(ex.Message);
}
if (addrs == null || addrs.Length == 0)
{
mIsIpv6Only = false;
return mIsIpv6Only.Value;
}
// 优先选用ipv4的模式
var haveIpv4 = false;
foreach (var addr in addrs)
{
if (addr.AddressFamily == AddressFamily.InterNetwork)
haveIpv4 = true;
else if (addr.AddressFamily == AddressFamily.InterNetworkV6)
mIsIpv6Only = true;
}
if (haveIpv4)
{
mIsIpv6Only = false;
}
return mIsIpv6Only.Value;
}
#endif
return false;
}
}
/// <summary>
/// 根据Dns情况自动创建基于 ipv6 或者 ipv4 的 AddressFamily
/// </summary>
public static AddressFamily AddressFamily
{
get
{
var ret = AddressFamily.InterNetwork;
if (IsIpv6Only)
ret = AddressFamily.InterNetworkV6;
return ret;
}
}
/// <summary>
/// 根据Dns情况自动创建基于 ipv6 或者 ipv4的Socket
/// </summary>
/// <param name="protocolType"></param>
/// <param name="socketType"> </param>
/// <returns></returns>
public static Socket createSocket(ProtocolType protocolType, SocketType socketType = SocketType.Stream)
{
return new Socket(AddressFamily, socketType, protocolType);
}
/// <summary>
/// 转换ip (ipv6用ipv6的ip地址)
/// </summary>
/// <param name="srcIp"></param>
/// www.qq.com return www.qq.com
/// fe80:fe80::fe80... return fe80:fe80::fe80...
/// 182.254.139.22 retun 64:ff9b::b6fe:8b16
/// <returns></returns>
public static string praseIPStr(string srcIp)
{
if (!IsIpv6Only)
return srcIp;
if (!Regex.IsMatch(srcIp, @"((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))"))
return srcIp;
return praseIpv4ToIpv6(srcIp);
}
/// <summary>
/// ipv4 to ipv6
/// </summary>
/// <param name="srcIp"></param>
/// <returns></returns>
private static string praseIpv4ToIpv6(string srcIp)
{
var values = srcIp.splitToIntArray('.');
if (values == null || values.Length != 4)
return srcIp;
var ret = "64:ff9b::";
ret += Convert.ToString(values[0], 16);
ret += Convert.ToString(values[1], 16);
ret += ":";
ret += Convert.ToString(values[2], 16);
ret += Convert.ToString(values[3], 16);
return ret;
}
}
结语
对网络通信这一块还不太熟悉,苹果的建议是不要使用低级api like socket…