Android DNS解析过程

17 篇文章 0 订阅

前言

一次排查接口404问题,引伸的Android DNS解析过程,简单分析总结一下

1、首先明白DNS解析流程

  1. 操作系统检查自身本地的hosts文件是否有这个网址的映射关系,如果有,直接返回完成域名解析如果hosts文件没有这个域名映射,则查找本地dns解析器缓存,如果有映射关系则完成域名解析
  2. 如果hosts和本地dns缓存都没有映射关系,则查找TCP/IP中的首选dns服务器(本地dns服务器),收到查询时,如果查询的资源在本地配置区域中,则返回解析地址给客户机,完成域名解析。
  3. 如果不在本地dns服务器区域解析,但是该服务器缓存了相关的信息,则从缓存中调用这个映射关系,完成解析,但是这个解析不具备权威性
  4. 如果本地dns服务器本地区域文件与缓存都解析失败,则根据本地dns服务器的设置进行查询,如果没有启用转发模式,本地dns就把请求发给13台根dns,根dns判断这个域名所述的顶级域名服务器,并返回负责该顶级域名的服务器的一个IP地址。本地dns服务器收到ip信息后联系对应的顶级域的这台服务器。这台负责顶级域的服务器收到请求后,如果自己无法解析,它会找到下一集dns的地址并返回给本地dns服务器。当本地dns服务器收到这个地址后,就会取新的服务器上查询一直重复这个动作,直到找到主机
  5. 如果使用了转发模式,此dns服务器会把请求转发至上一级dns服务器,由上一级服务器进行解析如果也不能解析则继续转发到上机,知道找到能够解析的服务器

2、Java DNS查询内部实现

在Android的DNS操作API同样是Java的InetAddress 来实现的。

jshell> InetAddress.getByName("baidu.com").getHostAddress()
$1 ==> "220.181.38.148"

jshell> InetAddress.get
getAllByName(          getByAddress(          getByName(             getLocalHost()         getLoopbackAddress()

jshell> InetAddress.getAllByName("baidu.com")
$2 ==> InetAddress[2] { baidu.com/220.181.38.148, baidu.com/220.181.38.251 }

域名反查,百度IP还是返回IP,DNS欧凯
InetAddress[] addresses = InetAddress.getAllByName("8.8.8.8");
addresses[0].getHostName();
$6 ==> "dns.google"

打开IDE,看看源码和doc,也很简单明了。

nameServices 是真正的DNS查询;并依次解析

DNSNameService 是默认查询实现,通过spi, jndi 方式可以指定DNS服务器,不过只限Java

/**
     * 获取DNS服务器信息
     *
     * @param domain     要获取DNS信息的域名
     * @param provider   DNS服务器
     * @param types      信息类型 "A"(IP信息),"MX"
     * @param timeout    请求超时
     * @param retryCount 重试次数
     * @return 所有信息组成的数组
     * @throws NamingException
     */

    @SuppressWarnings("rawtypes")
    public static ArrayList<String> getDNSRecs(String domain, String provider,
                                               String[] types, int timeout, int retryCount) throws NamingException, NamingException {
        ArrayList<String> results = new ArrayList<String>(15);
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial",
                "com.sun.jndi.dns.DnsContextFactory");
        //设置域名服务器
        env.put(Context.PROVIDER_URL, "dns://" + provider);
        // 连接时间
        env.put("com.sun.jndi.dns.timeout.initial", String.valueOf(timeout));
        // 连接次数
        env.put("com.sun.jndi.dns.timeout.retries", String.valueOf(retryCount));
        DirContext ictx = new InitialDirContext(env);
        Attributes attrs = ictx.getAttributes(domain, types);
        for (Enumeration e = attrs.getAll(); e.hasMoreElements(); ) {
            Attribute a = (Attribute) e.nextElement();
            int size = a.size();
            for (int i = 0; i < size; i++) {
                results.add((String) a.get(i));
            }
        }
        return results;
    }
    /**
     * 获取域名所有IP
     *
     * @param domain     域名
     * @param dnsServers DNS服务器列表
     * @param timeout    请求超时
     * @param retryCount 重试次数
     * @return
     */
    public static Set<String> getAllIP(String domain, String[] dnsServers,
                                       int timeout, int retryCount) {
        Set<String> ips = new HashSet<String>();
        for (String dnsServer : dnsServers) {
            List<String> ipList;
            try {
                ipList = getDNSRecs(domain, dnsServer, new String[]{"A"},
                        timeout, retryCount);
            } catch (NamingException e) {
                continue;
            }
            ips.addAll(ipList);
        }
        return ips;
    }

3、Android DNS查询内部实现

追踪InetAddress 分析,是netbsd 来解释DNS的,Libcore.os.getaddrinfo 为主要调用函数

Android系统采用的是一个从BSD继承而来的标准的系统函数库bionic

bionic/
|——libc//C库
|——libstdc++ //C++实现库
|——libthread_db //线程库

Android DNS 代码都在bionic/libc/netbsd中、(虽然netbsd 是个废弃的项目,但dns功能部分代码被 Android用上了, 真牛逼,代码晦涩难懂)。想了解的同学,查查源码,这里推荐不错的文章Android okhttp3 DNS 底层实现追踪

4、网络优化优化HTTPDNS

我们知道DNS解析通常采用的UDP。UDP基于无连接传输,所以传输效率高。
TCP响应时间=TCP连接时间 + DNS查询时间;
UDP响应时间=DNS查询时间;

移动解析HTTPDNS 则基于 HTTP 协议向xx云的 DNS 服务器发送域名解析请求,替代了基于 DNS 协议向运营商 Local DNS 发起解析请求的传统方式,可以避免 Local DNS 造成的域名劫持和跨网访问问题,解决移动互联网服务中域名解析异常带来的困扰。更有效地保障您的业务正常,避免移动互联网中的劫持、跨网域名解析错误等问题。

安卓上要实现自己DNS解析,工作量还不少,建议看看dnsjavaminidns 这些实现库

基于HTTPDNS的+OKHTTP,倒是很舒服就能接入成功,但是我们最好了解整个过程。

简化

  • 域名对应的IP地址
  • OKHTTP DNS接口,返回InetAddress[] 对象
byte[] bytes = textToNumericFormatV4("220.181.38.251");
        try {
            InetAddress byAddress = Inet4Address.getByAddress("baidu.com", bytes);
            System.out.println(byAddress instanceof Inet4Address);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

如果不是OKHTTP,可以参考阿里云的解决方案Android端HTTPS(含SNI)业务场景:IP直连方案

另外,aliyun httpdns android-sdk ,是个HTTPDNS 实现很好的参考sdk源码,可见这块优化,必须采用成熟的方案。

常用共有云DNS

名称首选地址备选地址
114 DNS114.114.114.114114.114.115.115
阿里DNS223.5.5.5223.6.6.6
百度DNS180.76.76.76

参考网站

cloudflare&DNS

一文看懂网络七层协议/OSI七层模型

cloudflare&DNS

diggui

windows & linux dig download

ping & net

Java DNS查询内部实现

jndi

Local Managed DNS (Java)

dnsjava

Android okhttp3 DNS 底层实现追踪

在 Android 上解析 DNS SRV 记录的轻量级方法

DNS : Java Glossary

minidns

HTTPDNS

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OkHttp是一个用于AndroidJava应用程序的HTTP客户端库。当使用OkHttp进行网络请求时,它会自动解析主机名并将其转换为IP地址。这个过程被称为DNS解析。默认情况下,OkHttp使用系统默认的DNS解析器来解析主机名。 但是,你也可以使用自定义的DNS解析器来替换系统默认的DNS解析器。这可以通过OkHttp的Dns接口实现。你只需要实现Dns接口并将其传递给OkHttpClient即可。 例如,你可以创建一个自定义的DNS解析器,它将所有主机名解析为特定的IP地址: ``` public class CustomDns implements Dns { private static final InetAddress[] CUSTOM_IPS = new InetAddress[] { InetAddress.getByName("192.168.0.1"), InetAddress.getByName("192.168.0.2") }; @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { return Arrays.asList(CUSTOM_IPS); } } ``` 然后,你可以将这个自定义的DNS解析器传递给OkHttpClient: ``` OkHttpClient client = new OkHttpClient.Builder() .dns(new CustomDns()) .build(); ``` 这样,在进行网络请求时,OkHttp将使用你自定义的DNS解析器来解析主机名。 ### 回答2: OkHttp DNS 是 OkHttp 框架中的一部分,用于处理网络请求时解析域名的功能。 在进行网络请求时,我们通常会使用域名来表示目标主机,如 "www.example.com"。然而,计算机网络通信实际上是通过 IP 地址进行的。因此,我们需要将域名解析为对应的 IP 地址,以便与服务器进行通信。 OkHttp DNS 提供了一种机制,可以自定义和配置域名解析的策略。它允许开发人员替换默认的 DNS 解析器,从而实现更高效、灵活的域名解析。 OkHttp 内置了一个默认的 DNS 解析器,它使用 Java 标准库中的 InetAddress 类来解析 IP 地址。然而,对于某些特定的场景和需求,我们可能需要使用其他的 DNS 解析器。 通过实现自定义的 Dns 接口,我们可以将其传递给 OkHttp,从而替换默认的 DNS 解析器。我们可以自己实现域名解析的逻辑,例如在本地缓存 IP 地址、使用其他第三方 DNS 解析库等。这样可以提高解析效率、减少网络请求的延迟。 在使用 OkHttp DNS 时,我们可以通过 OkHttpClient.Builder 类的 dns() 方法来设置自定义的 DNS 解析器。例如: Dns customDns = new CustomDns(); OkHttpClient client = new OkHttpClient.Builder() .dns(customDns) .build(); 通过上述代码,我们将自定义的 DNS 解析器 customDns 设置为 OkHttpClient 的属性。在后续的网络请求中,OkHttp 将使用该解析器来解析域名。 总之,OkHttp DNS 提供了灵活、可定制的域名解析策略,可以满足不同场景下的需求,提升网络请求的性能。 ### 回答3: OkHttp是一个开源的轻量级的HTTP客户端库,主要用于Android平台。它提供了一组接口来发送和接收HTTP请求和响应数据,并且支持各种常见的HTTP协议和功能。 OkHttp DNS是OkHttp库提供的一个功能,用于解析主机名(域名)并将其转换为IP地址。DNS(Domain Name System)是互联网中用于将域名转换为IP地址的系统,它允许我们使用域名来访问Web服务器,而不是直接使用IP地址。 OkHttp默认使用系统的DNS解析服务来解析主机名。但是,OkHttp也提供了自定义DNS功能,使我们可以根据需要修改或扩展DNS解析过程。 通过使用OkHttp DNS,我们可以实现以下功能: 1. 自定义DNS解析:我们可以使用自定义的DNS解析服务来解析主机名。这对于需要使用特定的DNS服务器或实现一些自定义逻辑的应用程序非常有用。 2. 域名解析优化:OkHttp DNS还可以实现域名解析的优化。例如,我们可以缓存最近解析的域名,以便下次访问时可以更快地获取IP地址。这可以提高应用程序的性能和响应速度。 3. DNS解析故障处理:如果默认的DNS解析服务发生故障或无法解析某个域名,我们可以使用OkHttp DNS来处理这些错误情况。例如,我们可以在DNS解析失败时使用备用的DNS服务器。 总之,OkHttp DNS是一个非常有用的功能,它可以让我们更灵活地处理主机名的解析,并提供更好的性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值