Unity接入移动解析HTTPDNS SDK最佳方案

本文档介绍了Unity场景下的接入移动解析HTTPDNS的方案。

一、概述

Unity是内容创作引擎。Unity可以开发:2D/3D游戏,2D/3D应用,VR,AR,甚至移动App,网页前端 ,电影实时渲染等,创作者可以借助Unity将创意变成现实。

为方便 Unity 开发者接入使用移动解析HTTPDNS SDK,我们提供了针对Unity场景下的PdnsUnityDemo源码供用户参考,源码中提供了移动解析HTTPDNS Unity插件。

重要

PdnsUnityDemo使用的是Unity 2019.4.21f1c1开发版本,Assets/Plugins目录下为插件(用户也可根据实际情况使用阿里云DNS SDK来制作插件),插件使用了定制的Gradle文件和Proguard文件,如果您的项目有其他地方也定制了这些文件,将内容合并即可。

二、集成方式

  1. 导入Plugins文件。

PdnsUnityDemo中 Plugins 文件夹中的文件复制到您 Unity 工程>Assets>Plugins 文件夹中。

  1. 配置参数。

AlipdnsHelper.setAccountIdAndSetAccessKeyIdAndSetAccesskeySecret("accountId","accessKeyId","accesskeySecret");
AlipdnsHelper.setCacheEnable(true);
AlipdnsHelper.setSchedulePrefetchEnable(true);
AlipdnsHelper.setIspEnable(true);
AlipdnsHelper.setMaxCacheTTL(3600);
AlipdnsHelper.setMaxNegativeCache(60);
AlipdnsHelper.setScheme(1);
AlipdnsHelper.setShortEnable(false);
AlipdnsHelper.setSpeedTestEnable(true);
AlipdnsHelper.setCacheCountLimit(100);
AlipdnsHelper.setSpeedPort(80);
// 预解析域名
List<string> list = new List<string>();
list.Add("域名1");
list.Add("域名2");
AlipdnsHelper.setPreloadDomains(list);

三、API介绍

接口类为AlipdnsHelper.cs。

  1. 必传参数,accountId,您在控制台注册后,控制台会生成唯一标识Account ID。

  2. 必传参数,accessKeyId,accessKeySecret 在控制台开启鉴权后生成该参数,设置鉴权功能来保障用户身份安全,不被第三方未授权者盗用。

public static void setAccountIdAndSetAccessKeyIdAndSetAccesskeySecret(string accountId,string accessKeyId,string accessKeySecret)
  1. 设置开启缓存,在第一次解析过域名后,后续再解析时, 优先获取缓存中的数据,可大大提高解析速度。

public static void setCacheEnable(bool enable)
  1. 设置是否开启定时主动更新过期缓存,开启后SDK每分钟会自动更新过期缓存,保障用户缓存数据及时更新,但是可能会带来域名解析次数和客户端流量消耗的增多。

public static void setSchedulePrefetchEnable(bool enable)
  1. 设置是否开启依据ISP网络区分域名缓存,开启后则在不同网络环境下域名缓存数据分别存储互不影响。如果不开启,则在不同网络下使用同一份域名缓存数据。

public static void setIspEnable(bool enable)
  1. 设置缓存最大TTL时间,如果设置了该时间,则会限制缓存的最大TTL不超过该设置时间。

    SDK默认该参数为3600s。

public static void setMaxCacheTTL(double maxCacheTTL)
  1. 设置否定缓存最大TTL时间,设置了该时间,则会限制否定缓存的最大TTL不超过该设置时间。

    SDK默认该参数为30s。

public static void setMaxNegativeCache(double maxNegativeCache)
  1. 在访问DNS服务器进行解析时,是通过HTTP协议解析,还是通过HTTPS协议解析,可通过scheme属性进行设置。scheme默认是0,使用HTTP协议进行解析,建议使用HTTP协议进行解析,解析速度更快,scheme设为1即使用HTTPS协议进行解析。

public static void setScheme(int scheme)
  1. 设置是否开启short模式,移动解析HTTPDNSDoH JSON API返回数据分为全量JSON和简要IP数组格式。SDK默认为全量JSON格式。

public static void setShortEnable(bool enable)
  1. 设置是否开启IP优选,开启后解析结果数组会按照测速结果由快到慢的顺序排列。

public static void setSpeedTestEnable(bool enable)
  1. 设置缓存数量,可以自定义缓存数量(支持范围100~500之间)。

public static void setCacheCountLimit(int cacheCountLimit)
  1. 设置IP测速优选端口,设置socket探测指定端口。

public static void setSpeedPort(int speedPort)
  1. 由于SDK自带缓存功能,在第一次解析完域名后,后续再次解析此域名可大大提高解析速度,所以建议在app启动后,可对app中可能要解析的域名进行预加载。

public static void setPreloadDomains(List<string> hosts)
  1. 自动感知网络环境(ipv4-only、ipv6-only、ipv4ipv6双栈)直接从缓存中获取适用于当前网络环境的IP数组,无需等待。如无缓存,或有缓存但已过期并且expiredIPEnabledfalse,则返回空。

@param host 域名

@param expiredIPEnabled 是否允许返回过期ip

public static List<string> getIpsByCacheWithDomain(string host, bool expiredIPEnabled)
  1. 直接从缓存中获取ipv4解析结果,无需等待. 如无缓存,或有缓存但已过期,并且expiredIPEnabledfalse,则返回空。

@param host 域名

@param expiredIPEnabled 是否允许返回过期ip

public static List<string> getIpv4ByCacheWithDomain(string host, bool expiredIPEnabled)
  1. 直接从缓存中获取ipv6解析结果,无需等待. 如无缓存,或有缓存但已过期,并且expiredIPEnabledfalse,则返回空。

@param host 域名

@param expiredIPEnabled 是否允许返回过期ip

public static List<string> getIpv6ByCacheWithDomain(string host, bool expiredIPEnabled)
  1. 获取请求移动解析HTTPDNS成功失败统计信息。

public static string getRequestReportInfo()

四、最佳实践

4.1 原理说明

本示例展示了Unity中集成HTTPDNS的完整解决方案:

  1. 跨平台统一接口:使用 AlipdnsHelper 提供统一的API接口

  2. 平台特定实现:针对 iOS、Android、 SDK提供不同的底层实现

  3. 自动DNS替换:在网络请求前自动将域名替换为解析的IP地址

  4. Host头设置:确保HTTPS/SSL连接的SNI正确性

4.2 网络请求集成

当使用不同的网络模块发送请求时,需要相应地进行请求头配置等操作,下面对HttpClient、HttpWebRequest、UnityWebRequest三种类型分别给出示例:

4.2.1 HttpClient

using System;
using System.Net.Http;
using UnityEngine;

public class AlipdnsHttpClient : MonoBehaviour
{
    private static readonly HttpClient httpClient = new HttpClient();
    
    public async void MakeRequest(string url)
    {
        try
        {
            var uri = new Uri(url);
            string hostname = uri.Host;
            
            // 使用HTTPDNS解析域名
	    var result = AlipdnsHelper.getIpsByCacheWithDomain(host, true);
            if (result != null && result.Count > 0)
            {
                string resolvedIP = result[0];
                string newUrl = url.Replace(hostname, resolvedIP);
                
                using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, newUrl))
                {
                    // 关键:设置Host头保证SSL/SNI正确性
                    requestMessage.Headers.Host = hostname;
                    
                    var response = await httpClient.SendAsync(requestMessage);
                    string content = await response.Content.ReadAsStringAsync();
                    
                    Debug.Log($"请求成功: {response.StatusCode}");
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogError($"请求失败: {e.Message}");
        }
    }
}

4.2.2 HttpWebRequest

using System;
using System.IO;
using System.Net;
using UnityEngine;

public class AlipdnsWebRequest : MonoBehaviour
{
    public void MakeRequest(string url)
    {
        try
        {
            var uri = new Uri(url);
            string hostname = uri.Host;
            
            // 使用HTTPDNS解析域名
            var result = AlipdnsHelper.getIpsByCacheWithDomain(host, true);
            if (result != null && result.Count > 0)
            {
                string resolvedIP = result[0];
                string newUrl = url.Replace(hostname, resolvedIP);
                
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(newUrl);
                request.Method = "GET";
                // 关键:设置Host头保证SSL/SNI正确性
                request.Host = hostname;
                
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                using (Stream stream = response.GetResponseStream())
                using (StreamReader reader = new StreamReader(stream))
                {
                    string content = reader.ReadToEnd();
                    Debug.Log($"请求成功: {response.StatusCode}");
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogError($"请求失败: {e.Message}");
        }
    }
}

4.2.3 UnityWebRequest(不推荐)

重要

由于UnityWebRequest无法正确配置SNI(服务器域名指示)信息,当服务器依赖SNI返回特定域名证书时,会导致SSL验证失败。因此处理这类域名时,推荐使用前两种网络请求方式。

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class AlipdnsUnityWebRequest : MonoBehaviour
{
    public IEnumerator MakeRequest(string url)
    {
        var uri = new Uri(url);
        string hostname = uri.Host;
        
        // 使用HTTPDNS解析域名
        var result = AlipdnsHelper.getIpsByCacheWithDomain(host, true);
        if (result != null && result.Count > 0)
        {
            string resolvedIP = result[0];
            string newUrl = url.Replace(hostname, resolvedIP);
            
            using (UnityWebRequest request = UnityWebRequest.Get(newUrl))
            {
                // 关键:设置Host头
                request.SetRequestHeader("Host", hostname);
                
                yield return request.SendWebRequest();
                
                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log($"请求成功: {request.responseCode}");
                }
                else
                {
                    Debug.LogError($"请求失败: {request.error}");
                }
            }
        }
    }
}

说明
  1. 当前文档只针对Unity场景下移动解析HTTPDNS Android/iOS SDK的使用。

  2. 关于移动解析HTTPDNS Android/iOS SDK的接入与使用问题,请查看Android SDK开发指南iOS SDK开发指南

  3. 开发者在Unity场景下接入移动解析HTTPDNS Android/iOS SDK实践完整代码请参考PdnsUnityDemo源码