在通过DNS查找域名的过程中,可能会经过多台中间DNS服务器才能找到指定的域名,因此,在DNS服务器上查找域名是非常昂贵的操作。在Java中为了缓解这个问题,提供了DNS缓存。当InetAddress类第一次使用某个域名(如www.csdn.net)创建InetAddress对象后,JVM就会将这个域名和它从DNS上获得的信息(如IP地址)都保存在DNS缓存中。当下一次InetAddress类再使用这个域名时,就直接从DNS缓存里获得所需的信息,而无需再访问DNS服务器。

DNS缓存在默认时将永远保留曾经访问过的域名信息,但我们可以修改这个默认值。一般有两种方法可以修改这个默认值:
1. 在程序中通过java.security.Security.setProperty方法设置安全属性networkaddress.cache.ttl的值(单位:秒)。如下面的代码将缓存超时设为10秒:
java.security.Security.setProperty("networkaddress.cache.ttl", 10);
2. 设置java.security文件中的networkaddress.cache.negative.ttl属性。假设JDK的安装目录是C:\jdk1.6,那么java.security文件位于c:\jdk1.6\jre\lib\security目录中。打开这个文件,找到networkaddress.cache.ttl属性,并将这个属性值设为相应的缓存超时(单位:秒)。
    如果将networkaddress.cache.ttl属性值设为-1,那么DNS缓存数据将永远不会释放。下面的代码演示了使用和不使用DNS缓存所产生效果:
package mynet;

import java.net.*;

public class MyDNS
{
    public static void main(String[] args) throws Exception
    {
        // args[0]: 本机名 args[1]:缓冲时间
        if (args.length < 2)
            return;
        java.security.Security.setProperty("networkaddress.cache.ttl", args[1]);
        long time = System.currentTimeMillis();
        InetAddress addresses1[] = InetAddress.getAllByName(args[0]);
        System.out.println("addresses1:   "
                        + String.valueOf(System.currentTimeMillis() - time)
                        + "毫秒");
        for (InetAddress address : addresses1)
            System.out.println(address);
        System.out.print("按任意键继续");
        System.in.read();
        time = System.currentTimeMillis();
        InetAddress addresses2[] = InetAddress.getAllByName(args[0]);
        System.out.println("addresses2:   "
                        + String.valueOf(System.currentTimeMillis() - time)
                        + "毫秒");
        for (InetAddress address : addresses2)
            System.out.println(address);
    }
}

    在上面的代码中设置了DNS缓存超时(通过args[1]参数),用户可以通过命令行参数将这个值传入MyDNS中。这个程序首先使用getAllByName建立一个InetAddress数组,然后通过System.in.read使程序暂停。当用户等待一段时间后,可以按任意键继续,并使用同一个域名(args[0])再建立一个InetAddress数组。如果用户等待的这段时间比DNS缓存超时小,那么无论情况如何变化,addresses2和addresses1数组中的元素是一样的,并且创建addresses2数组所花费的时间一般为0毫秒(小于1毫秒后,Java无法获得更精确的时间)。
   测试1
执行如下命令(将DNS缓存超时设为5秒):
java mynet.MyDNS www.126.com 5
运行结果1(在5秒之内按任意键):
addresses1:   344毫秒
www.126.com/202.108.9.77
按任意键继续
addresses2:  0毫秒
www.126.com/202.108.9.77
运行结果2(在5秒后按任意键):
addresses1:   344毫秒
www.126.com/202.108.9.77
按任意键继续
addresses2:  484毫秒
www.126.com/202.108.9.77
在上面的测试中可能出现两个运行结果。如果在出现“按任意键继续…”后,在5秒之内按任意键继续后,就会得到运行结果1,从这个结果可以看出,addresses2所用的时间为0毫秒,也就是说,addresses2并未真正访问DNS服务器,而是直接从内存中的DNS缓存得到的数据。当在5秒后按任意键继续后,就会得到运行结果2,这时,内存中的DNS缓存中的数据已经释放,所以addresses2还得再访问DNS服务器,因此,addresses2的时间是484毫秒(addresses1和addresses2后面的毫秒数可能在不同的环境下的值不一样,但一般情况下,运行结果1的addresses2的值为0或是一个接近0的数,如5。运行结果2的addresses2的值一般会和addresses1的值很接近,或是一个远比0大的数,如1200)。
测试2
执行如下命令(ComputerName为本机的计算机名,DNS缓存超时设为永不过期[-1]):
java mynet.MyDNS ComputerName -1
运行结果(按任意键继续之前,将192.168.18.20删除):
addresses1:   31毫秒
myuniverse/192.168.18.10
myuniverse/192.168.18.20
按任意键继续
addresses2:   0毫秒
myuniverse/192.168.18.10
myuniverse/192.168.18.20
     从上面的测试可以看出,将DNS缓存设为永不过期后,无论过多少时间,按任意键后,addresses2任然得到了两个IP地址(192.168.18.10和192.168.18.20),而且addresses2的时间是0毫秒,但在这时192.168.18.20已经被删除。因此可以判断,addresses2是从DNS缓存中得到的数据。如果运行如下的命令,并在5秒后按任意键继续后,addresses2就会只剩下一个IP地址(192.168.18.10)。
java mynet.MyDNS ComputerName 5
如果域名在DNS服务器上不存在,那么客户端在进行一段时间的尝试后(平均为5秒),就会抛出一个UnknownHostException异常。为了让下一次访问这个域名时不再等待,DNS缓存将这个错误信息也保存了起来。也就是说,只有第一次访问错误域名时才进行5称左右的尝试,以后再访问这个域名时将直接抛出UnknownHostException异常,而无需再等待5秒钟,
访问域名失败的原因可能是这个域名真的不存在,也可能是因为DNS服务器或是其他的硬件或软件的临时故障,因此,一般不能将这个域名错误信息一直保留。在Java中可以通过networkaddress.cache.negative.ttl属性设置保留这些信息的时间。这个属性的默认值是10秒。它也可以通过java.security.Security.setProperty方法或java.security文件来设置。下面的代码演示了networkaddress.cache.negative.ttl属性的用法:
package mynet;

import java.net.*;

public class MyDNS1
{
    public static void main(String[] args) throws Exception
    {
        java.security.Security.setProperty("networkaddress.cache.negative.ttl",
                        "5");
        long time = 0;
        try
        {
            time = System.currentTimeMillis();
            InetAddress.getByName("www.ppp123.com");
        }
        catch (Exception e)
        {
            System.out.println("www.ppp123.com不存在! address1: "
                            + String.valueOf(System.currentTimeMillis() - time)
                            + "毫秒");
        }
        //Thread.sleep(6000); // 延迟6秒
        try
        {
            time = System.currentTimeMillis();
            InetAddress.getByName("www.ppp123.com");
        }
        catch (Exception e)
        {
            System.out.println("www.ppp123.com不存在! address2: "
                            + String.valueOf(System.currentTimeMillis() - time)
                            + "毫秒");
        }
    }
}

在上面的代码中将networkaddress.cache.negative.ttl属性值设为5秒。这个程序分别测试了address1和address2访问www.ppp123.com(这是个不存在的域名,读者可以将其换成任何不存在的域名)后,用了多长时间抛出UnknownHostException异常。
运行结果:
www.ppp123.com不存在! address1:  4688毫秒
www.ppp123.com不存在! address2:  0毫秒
    我们从上面的运行结果可以看出,address2使用了0毫秒就抛出了异常,因此,可以断定address2是从DNS缓存里获得了域名www.ppp123.com不可访问的信息,所以就直接抛出了UnknowHostException异常。如果将上面代码中的延迟代码的注释去掉,那么可能得到如下的运行结果:
www.ppp123.com不存在! address1:  4688毫秒
www.ppp123.com不存在! address1:  4420毫秒
从上面的运行结果可以看出,在第6秒时,DNS缓存中的数据已经被释放,因此,address2仍需要访问DNS服务器才能知道www.ppp123.com是不可访问的域名。
在使用DNS缓存时有两点需要注意:
1. 可以根据实际情况来设置networkaddress.cache.ttl属性的值。一般将这个属性的值设为-1。但如果访问的是动态映射的域名(如使用动态域名服务将域名映射成ADSL的动态IP), 就可能产生IP地址变化后,客户端得到的还是原来的IP地址的情况。
2. 在设置networkaddress.cache.negative.ttl属性值时最好不要将它设为-1,否则如果一个域名因为暂时的故障而无法访问,那么程序再次访问这个域名时,即使这个域名恢复正常,程序也无法再访问这个域名了。除非重新运行程序。

Java网络编程从入门到精通(4):DNS缓存相关推荐

  1. java消息头,Java网络编程从入门到精通:HTTP消息头字段

    Java网络编程从入门到精通:HTTP消息头字段 一.通用头字段 1. Connection 这个字段只在HTTP1.1协议中存在.它决定了客户端和服务器进行了一次会话后, 服务器是否立即关闭网络连接 ...

  2. Java网络编程从入门到精通(1):Internet地址概述

    所有连入Internet的终端设备(包括计算机.PDA.打印机以及其他的电子设备)都有一个唯一的索引,这个索引被称为IP地址.现在Internet上的IP地址大多由四个字节组成,这种IP地址叫做IPv ...

  3. Java网络编程从入门到精通(14):多种多样的建立网络连接的方式

    在上一篇文章中我们讨论了Socket类的基本用法,并给出的例子中使用Socket类连接服务器时使用了一种最简单的连接方式,也就是通过IP和端口号来连接服务器.而为了使连接服务器的方式更灵活,Socke ...

  4. Java网络编程从入门到精通(25):创建ServerSocket对象

    ServerSocket类的构造方法有四种重载形式,它们的定义如下: public ServerSocket() throws IOException public ServerSocket(int  ...

  5. Java网络编程从入门到精通 (9):使用isXxx方法判断地址类型

     IP地址分为普通地址和特殊地址.在前面的文章中所使用的大多数都是普通的IP地址,在本文中将介绍如何利用InetAddress类提供的十个方法来确定一个IP地址是否是一个特殊的IP地址. 一.isAn ...

  6. Java网络编程从入门到精通(24):实现HTTP断点续传下载工具(附源代码)

    源代码下载:download.rar 在前面的文章曾讨论了HTTP消息头的三个和断点继传有关的字段.一个是请求消息的字段Range,另两个是响应消息字段Accept-Ranges和Content-Ra ...

  7. Java网络编程从入门到精通(2):创建InetAddress对象的四个静态方法

    InetAddress类是Java中用于描述IP地址的类.它在java.net包中.在Java中分别用Inet4Address和Inet6Address类来描述IPv4和IPv6的地址.这两个类都是I ...

  8. Java网络编程从入门到精通(3):为什么不能直接通过IP访问网站

     在<创建InetAdrress对象的四个静态方法>一文中通过getAllByName得到了www.csdn.net对应的四个IP地址.从理论上说,在IE(或其他的Web浏览器,如Fire ...

  9. Java网络编程从入门到精通(7):用getHostAddress方法获得IP地址

     这个方法用来得到主机的IP地址,这个IP地址可能是IPv4的地址,也可能是IPv6的地址.getHostAddress方法的定义如下: public String getHostAddress() ...

最新文章

  1. pygame 移开的矩形留痕迹_Python之pygame学习矩形区域(5)
  2. 对E—R模型的深入理解
  3. redis、memcache、mongoDB 对比
  4. android4.0 开机启动activity 4.0,如何正确理解和使用Activity的4种启动模式
  5. Java内存泄漏介绍
  6. python 40位的数减个位数_Python——进制表示与转换
  7. php实现双向队列详解,PHP实现一个双向队列例子
  8. python xlrd导入后怎么保存_Python xlrd模块导入过程及常用操作
  9. java编程两个超长正整数相减_【每日编程237期】数字分类
  10. iOS中转义后的html标签如何还原
  11. lievent源码分析:evbuffer
  12. docker安装gamit_ubuntun10.10中安装gamit 10.40
  13. 朱石景 201671010457 团队项目评审课程学习总结
  14. css3 修改checkbox disabled颜色_HTML5 + CSS3 gt;gt;gt; 009
  15. 手机股票软件哪个好?这几款炒股app你不能错过!
  16. 时间片轮转调度算法模拟C语言
  17. 《高等代数学》读书笔记前言
  18. 数字图像处理 拜耳过滤器简介
  19. JAVA课程设计——彩票抽奖购买系统
  20. QQ使用的一个小问题

热门文章

  1. No Database Selected
  2. 分析分布式服务框架理论介绍
  3. 多线程和多进程的差别(小结)
  4. 电脑电视兼容成科技行业新课题
  5. 索引贴——移动开发(.Net CF 停止更新)
  6. Channel延续篇
  7. (FortiGate)飞塔防火墙防病毒解决方案
  8. PL/SQL语言基础
  9. 解析EXCEL CSV格式文件的方法
  10. ( KMP 求循环节的个数)Power Strings -- poj -- 2406