如何更新手机?dns缓存文件 (如何刷新手机dns缓存)

作者:默默点滴

出处:https://www.mobibrw.com/2020/28412

一、什么是DNS

DNS(Domain Name System,域名系统),dns用于域名解析分析ip地址。

例如:给你www.baidu.com你给主机名

我找到了对应的ip地址:163.177.151.109。

例如:给你www.baidu.com你给主机名

我找到了对应的ip地址:163.177.151.109.有些主机名也会有别名,比如www.baidu.com就

有别名www.a.shifen.com,甚至不止一个别名,或者一个别名有两个ip地址。在linux机子上,运行nslookup(name service lookup)是域名分析。如下面:

~$ nslookup www.baidu.comServer:        127.0.0.1Address:        127.0.0.1#53 Non-authoritative answer:www.baidu.com  canonical name = www.a.shifen.com.Name:  www.a.shifen.comAddress: 163.177.151.109Name:  www.a.shifen.comAddress: 163.177.151.110

DNS分为递归查询和迭代查询,具体可参考下图

DNS也可用于负载均衡、域名污染、防火墙,这里不讨论。

二、DNS缓存

所谓DNS有两种缓存,如同步缓存和本地缓存缓存。对于手机来说,重点是本地缓存DNS缓存。

二、DNS缓存

所谓DNS有两种缓存,如同步缓存和本地缓存缓存。对于手机来说,重点是本地缓存DNS缓存。Android基于Linux系统,对于Android App这个缓存又多了,java层。

2.1 使用场景当然,我们需要理解Android App这些场景需要进行,这是最重要的,有时没有必要更新缓存。综上所述,这里的场景无非是以下几种:

场景一:存在多个运营商或者多个地区的分布式业务系统

例如,互联网分布式业务系统采用不同区域和运营商的方式,而不是业务系统。

场景二:存在多个域名的业务系统,需要提前分析和缓存ip

<link rel="dns-prefetch" href="//g.alicdn.com" /><link rel="dns-prefetch" href="//img.alicdn.com" /><link rel="dns-prefetch" href="//tui.taobao.com" />

这是taobao网的dns-prefetch link,这一步是加速其他页面dns场景三:ip地址是唯一的,但有多个子域名高并发请求

综上所述,我们可以理解为,只有域名和ip地址之间的关系是一对多、多对多和多对一DNS缓存。

2.2系统版本描述Android 4.3之前的TTL(Time To Live)正负有效期分为10分钟,最大缓存120个。TTL算法回收。

// 默认有效DNS缓存时间(TTL). 600 seconds (10 minutes).private static final long DEFAULT_POSITIVE_TTL_NANOS = 600 * 1000000000L; // 默认无效缓存时间(TTL). 10 seconds.private static final long DEFAULT_NEGATIVE_TTL_NANOS = 10 * 1000000000L;Android 4.3 的系统,缓存修正为2秒,最大缓存为16个LRU算法和TTL回收算法。

private static final long TTL_NANOS = 2 * 1000000000L;

注:见上述代码java.net.AddressCache.java三、Android DNS缓存更新

3.1.修改缓存过期时间在Android4.3之前,TTL可以用个System.setProperties设置,就可以了TTL修正为何Android 4.3 生存时间一致

Security.setProperty("networkaddress.cache.ttl", String.valueOf(2 * 1000000000L));  Security.setProperty("networkaddress.cache.negative.ttl", String.valueOf(2 * 1000000000L))

3.2 实现DNS-Prefetch

步骤3.1只缩短缓存过期时间,在一定程度上处理Android 4.3之前系统的不足。但是,域名和存在ip一对多、多对多和多对一的分布式系统,如果有网络切换,下次将被授予

可能

取还是比较费时的。但是,域名和存在ip一对多、多对多和多对一的分布式系统,如果有网络切换,下次将被授予可能取还是比较费时的。因此,预获取dns非常必要。那么如何实现呢?DNS-Prefetch呢

首先,我们需要统一规范接口

public interface Dns{   Dns SYSTEM = new Dns(){    @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException{      if (hostname == null) throw new UnknownHostException("hostname == null");      return Arrays.asList(InetAddress.getAllByName(hostname));    }  };   List<InetAddress> lookup(String hostname) throws UnknownHostException;}

实现接口public class DnsManager implements Dns{     private static DnsManager singleInstance;    private  final  TreeSet<String>  HOST_SET = new TreeSet<String>();     public static DnsManager getDefault(){        if(singleInstance==null){            synchronized (DnsManager.class)            {                if (singleInstance == null){                    singleInstance = new DnsManager();                }            }        }        return singleInstance;    }      @Override    public synchronized List<InetAddress> lookup(String hostname) throws UnknownHostException{        try{            if(TextUtils.isEmpty(hostname) || TextUtils.isEmpty(hostname.trim())){                throw new UnknownHostException("hostname == null");            }            List<InetAddress> list = Dns.SYSTEM.lookup(hostname);             HOST_SET.add(hostname);            return list;        }catch (Exception e){            e.printStackTrace();            return Arrays.asList(null);        }    }    public synchronized String quickLookup(String hostname) throws UnknownHostException {         try {            if(TextUtils.isEmpty(hostname) || TextUtils.isEmpty(hostname.trim())){                throw new UnknownHostException("hostname == null");            }            final Uri uri = Uri.parse(hostname);            InetAddress inetAddress = InetAddress.getByName(uri.getHost());            if(inetAddress==null) {                Throw.exception("unkown host",UnknownHostException.class);            }            String dnsIp = inetAddress.getHostAddress();            HOST_SET.add(hostname);            return  dnsIp;        } catch (Exception e) {            e.printStackTrace();            return Lists.newArrayList();        }    }     /**     * 清除dns缓存     */    public synchronized void clearDnsCache(){        try {            ReflectUtils.invokeMethodByName(InetAddress.class, "clearDnsCache");        }catch (Exception e){            e.printStackTrace();            return;        }    }     /**     * 获取主机集合     * @return     */    public synchronized  TreeSet<String> getHostSet() {        return HOST_SET;    }     /**     * 预加载DNS     * @param hosts     */    public synchronized void prefetchDns(List<String> hosts) {        if(hosts==null && hosts.size()==0) return;        for (String hostname:hosts ) {            prefetchDns(hostname);        }    }     /**     * 预加载DNS     * @param hostname     */    public synchronized void prefetchDns(String hostname) {        try{            InetAddress.getAllByName(hostname);        }catch (Exception e){            e.printStackTrace();            return;        }    }}

使用时机通常网络切换后,并且下次联网成功时,我们prefetch时最好的时间,这里我们需要通过Broadcast+IntentService

对于广播部分,我们需要监听如下两个Action(这里推荐使用动态广播)

IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

广播实现代码

public class NetStateChangeReceiver extends BroadcastReceiver{    private static final String TAG = NetStateChangeReceiver.class.getSimpleName();     private AtomicReference<String> pendingNetworkState = null;    private AtomicReference<String> pendingSSID = null;     public NetStateChangeReceiver() {        pendingNetworkState = new AtomicReference<String>();        pendingSSID = new AtomicReference<>();    }     @Override    public void onReceive(Context context, Intent intent) {        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {            NetworkType networkType = NetworkUtils.getNetworkType(context);            notifyObservers(networkType);        }        if(shouldStartDnsUpdateService(context,intent)) {            Intent cloneFilter = intent.cloneFilter();            cloneFilter.setClass(context, DnsUpdateIntentService.class);            context.startService(cloneFilter);        }    }    //网络可用并且网络切换的情况下启动IntentService更新    public boolean shouldStartDnsUpdateService(Context context,Intent intent){         if(NetworkUtils.isAvailable(context)){            NetworkType type = NetworkUtils.getNetworkType(context);            if(type==null) return false ;            String newState = type.toString();            String lastState = pendingNetworkState.get();            if(!TextUtils.isEmpty(lastState) && !lastState.equals(newState))            {                pendingNetworkState.set(newState);                return true;            }else{                pendingNetworkState.set(newState);                if(NetworkUtils.isWifiConnected(context)){                    WifiInfo wifiInfo= intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);                    if(wifiInfo!=null)                    {                        String nextSSID = wifiInfo.getSSID();                        String lastSSID = pendingSSID.get();                         if(nextSSID!=null && nextSSID.equals(lastSSID))                        {                            return true;                        }                    }                }             }        }else{            pendingNetworkState.set(NetworkType.NETWORK_NO.toString());        }        return false;    }}

DnsUpdateIntentService代码如下

public class DnsUpdateIntentService extends IntentService {     public DnsUpdateIntentService() {        super(DnsUpdateIntentService.class.getName());    }    @Override    protected void onHandleIntent(@Nullable Intent intent) {        runTask();    }    private void runTask() {       GFLog.d(DnsUpdateIntentService.class.getSimpleName()," startDns : 开始更新DNS ");        updateDnsCache();       GFLog.d(DnsUpdateIntentService.class.getSimpleName()," endDns : DNS更新完成 ");    }     private void updateDnsCache() {        try{            DnsManager dm = DnsManager.getDefault();            dm.clearDnsCache();            TreeSet<String> hostSet = dm.getHostSet();            List<String> hosts = new ArrayList<>();            hosts.addAll(hostSet);            dm.prefetchDns(hosts);        }catch (Exception e){            e.printStackTrace();            return;        }    } }

注意:DnsUpdateIntentService不可以注册为多进程,否则缓存无法更新

3.3、DNS防篡改与安全

Android 4.3之前的DNS可能存在被污染的可能,如修改resolv.conf文件,在Android 4.3+之后,统一使用Netd方式,安全性上有所提高。因此,对Android 4.3之前的系统,建议使用HttpDNS等方案,此外采取HTTPS的通信方式,一定程度上几乎可以绝对避免此类问题的发生。

此外,我们在

ip与域名对应数量 不大的app中,可以在App中提前建立不同机房的域名映射也是一种放置篡改的方案。3.4、Android底层DNS更新Android基于linux,底层通过Libcore.so更新DNS,目前没有方式来更新Linux层面的DNS缓存。那么,我们的DNS-Prefetch功能是否有必要呢?这个问题我们需要明确,虽然我们不一定能更新底层DNS,但是,可以促进底层DNS更新,类似System.gc()的作用。参考链接Android DNS更新与DNS-Prefetch基于OkHttp3 加入HttpDns功能作者:默默的点滴出处:https://www.mobibrw.com/2020/28412