Unity3D 支持 https 请求,兼容 ipv6 only 网络

(1)使用 HttpWebRequest 进行 Https 访问

Unity 2018 版本之后,如果要使用 https 访问,直接使用替代 www 的 UnityWebRequest 即可,访问 https 只需要指定 certificateHandler 即可进行 https 权限验证。

但是之前的 Unity 版本,用 UnityWebRequest 访问 https 会遇到权限问题,导致无法访问。所以,最好的办法是使用 .Net 的 HttpWebRequest,需要注意的是:打 Android 版本不能使用 .NET 2.0 subset,否则会找不到有关的 library,需要完整的 .NET 2.0

 1
 2
 3
 4
 5
 6
 7
 8
 9
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
43
44
//Https设置证书验证,配合 System.Net.WebRequest
ServicePointManager.ServerCertificateValidationCallback = 
    delegate (object s, X509Certificate cer, X509Chain chain, SslPolicyErrors sslError)
{
    return true;
};

//UnityWebRequest要在Unity2018以上才能支持https,这里用 HttpWebRequest 替代
string responseData = null;
HttpWebResponse response = null;
Stream dataStream = null;
try
{
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
    request.Method = "GET";
    request.Timeout = 2000;
    response = request.GetResponse() as HttpWebResponse;
    if (response != null)
    {
        dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        responseData = reader.ReadToEnd();
    }

    if (!string.IsNullOrEmpty(responseData))
    {
        Debug.LogFormat("Get Result {0}", responseData);
    }
    else
    {
        Debug.LogError("error");
    }
}
catch(Exception e)
{
    Debug.LogErrorFormat("Get Result Exception {0}", e)
}
finally
{
    if (response != null)
        response.Close();
    if(dataStream != null)
        dataStream.Close();
}

服务端要申请一个证书,一般是一个 .crt 文件(证书)和一个 .nopass(私钥) 文件,不同的服务端语言配置有所不同

(2)兼容 Ipv6 网络

所有 IP 地址要用域名替换,然后请求 DNS 得到真正的 IP 地址,在 IPV6 下得到的是 ipv6 地址,否则是 ipv4 地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public string ResolveDNS(string hostName)
{
    try
    {
        var addr = Dns.GetHostAddresses(hostName)
        if (addr != null && addr.Length > 0)
        {
            int index = Random.Range(0, addr.Length - 1);
            var a = addr[index];
            if (a != null)
            {
                return a.ToString();
            }
        }
    }
    catch(Exception e)
    {
        Debug.LogError("Exception " + e);
    }
    return string.Empty;
}

所有 TcpClient 等 Socket 连接对象需要传入 IPV6 的参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
TCpClient client = null;
IPAddress address;
if (!IPAddress.TryParse(host, out address))
{
    Debug.LogError("Connect TryParse host error:" + host);
    return;
}
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
    client = new TcpClient(AddressFamily.InterNetworkV6);
}
else
{
    client = new TcpClient(AddressFamily.InterNetwork);
}