InetAddress类概述与实例

来源:互联网 发布:老挝嫖尸妓院图片知乎 编辑:程序博客网 时间:2024/06/05 17:07

绝大部分知识与实例来自O’REILLY的《Java网络编程》(Java Network Programming,Fourth Edition,by Elliotte Rusty Harold(O’REILLY))。

InetAddress类简介

InetAddress类位于java.net包中,是Java对IP地址(包括IPv4和IPv6)的高层表示。许多其他网络相关类,包括Socket、ServerSocket、URL等都用到了这个类。这个类的每个实例包含一个IP地址(IP address)以及这个IP地址对应的主机名(host name)。

获取InetAddress实例

InetAddress类没有public的构造器,想要获取它的实例,需要通过它的一些静态工厂方法。最常用的一个是InetAddress.getByName(),能够根据域名或者IP地址获取InetAddress实例。这个方法会建立与本地DNS服务器的一个连接,并查找主机名和IP地址。如果DNS找不到这个地址,方法会抛出一个UnknownHostException,IOException的一个子类。

实例1:查询某个域名的IP地址和主机名

public static void ShowAddress(String domainName){    InetAddress address;    try {        address = InetAddress.getByName(domainName);        System.out.println(address);    } catch (UnknownHostException e) {        System.out.println("Could not find" + domainName);        e.printStackTrace();    }}public static void main(String[] args) {    ShowAddress("www.baidu.com");}输出:www.baidu.com/183.232.231.172

另外一个经常使用的是InetAddress.getLocalHost,用于返回运行代码的主机对应的InetAddress对象。

实例2:查找本地机器的地址

public static void ShowLocalAddress(){    try {        InetAddress address = InetAddress.getLocalHost();        System.out.println(address);    } catch (UnknownHostException e) {        System.out.println("Could not find this computer's address.");    }}public static void main(String[] args) {    ShowLocalAddress();}

另外,有时一个主机名可能有多个地址,如果想要全部获得,可以使用getAllByName()方法。该方法会返回一个InetAddress数组。
最后的两个构造器,用于根据原始IP地址和主机名构建一个InetAddress对象:

public static InetAddress getByAddress(byte[] addr) throws UnknownHostExceptionpublic static InetAddress getByAddress(String hostName,byte[] addr) throws UnknownHostException

需要注意三点:(1)参数中的主机名不一定能和IP地址对应。(2)byte[]遵循big-endian,即IP地址的高位存储在数组的低位。(3)Java中的byte为有符号字节,因此数值上不一定和int的值对应(比如168转为byte后值为-88)。
补充一下关于int和有符号字节的转换:有符号字节的取值范围为-128~127,而8位无符号整型的取值范围为0~255。转换方法如下:

  • 无符号整型转有符号字节:0~127不变,128~255用256减去原值。
  • 有符号字节转无符号整型:非负数不变,负数加上256。

缓存

由于DNS查找开销很大,InetAddress类会对查找结果进行缓存,一旦得到一个给定主机的地址,就不会再次查找。这样做可以使得可能会进行重复查找的程序的性能得到提升。不过,这种机制也带来了一些问题,比如无法察觉主机地址的改变等。
可以通过系统属性networkaddress.cache.ttl和networkaddress.cache.negative.ttl控制成功的DNS查找和失败的DNS查找的结果的缓存时间,若设置为-1则永不过期。

按IP地址查找

使用InetAddress.getByName传入一个IP地址作为参数时,不会检查DNS,只有当请求主机名(getHostName())时才会真正完成DNS查找。这意味着创建出来的InetAddress对象可能不对应任何主机。如果无法成功查找到主机名,主机名会和传入的IP地址保持一致,而不会抛出UnknownHostException异常
相对来讲,主机名比IP地址稳定的多。在构建InetAddress对象时,应优先考虑使用主机名作为参数传入。

get方法

InetAddress包含4个get方法,如下:

  • String getHostName():返回主机名(主机别名)
  • String getCanonicalHostName():返回主机全名
  • byte[] getAddress():获得IP地址的原始字节码
  • String getHostAddress():获得IP地址

主机全名是主机的全称,主机别名是为了方便好记等目的,为主机起的一个“昵称”。下面是用www.oracle.com构建的InetAddress对象调用getHostName()和getCanonicalHostName()的结果:

www.oracle.coma23-77-22-123.deploy.static.akamaitechnologies.com

至于查看原始字节码的方法,很多时候主要是用来判断协议类型(IPv4地址4字节、IPv6地址16个字节)。

实例3:判断IP地址使用的协议版本

public static int getIpVersion(InetAddress ia){    byte[] address = ia.getAddress();    if(address.length == 4){        return 4;    }else if (address.length == 16) {        return 6;    }else {        return -1;    }}public static void main(String[] args) {    try {        InetAddress ia = InetAddress.getByName("192.168.0.0");        System.out.println(getIpVersion(ia));    } catch (UnknownHostException e) {        e.printStackTrace();    }}输出:4

判断地址类型

有许多IP地址有着特殊的含义,比如127.0.0.1是本地回送地址,224.0.0.0~239.255.255.255是组播地址等。InetAddress类提供了以下方法对地址类别进行辨别:

  • isAnyLocalAddress():判断是否是通配地址。IPv4中通配地址为0.0.0.0,IPv6中通配地址为0:0:0:0:0:0:0:0(或写作::)。通配地址可以匹配本机系统中的任何地址。
  • isLoopbackAddress():判断是否是回送地址。IPv4中回送地址为127.0.0.1,IPv6中回送地址为0:0:0:0:0:0:0:1(或写作::1)。回送地址在网络层连接计算机和它自身。
  • isLinkLocalAddress():判断是否是IPv6本地链接地址。IPv6本地链接地址可以用于帮助IPv6网络实现自配置,与IPv4网络上的DHCP类似,但不需要使用服务器。路由器不会把发送给本地链接地址的包转发到本地子网以外。所有本地连接地址都用8字节FE80:0000:0000:0000开头,后8字节用本地地址填充,这个地址通常从以太网卡生产商分配的以太网MAC地址复制。
  • isSiteLocalAddress():判断是否是IPv6本地网站地址。本地网站地址与本地链接地址类似,不过本地网站地址可以由路由器在网站或校园内转发,但不应转发到网站以外。本地网站地址以8字节FEC0:0000:0000:0000开头,后8字节用本地地址填充,这个地址通常从以太网卡生产商分配的以太网MAC地址复制。
  • isMulticastAddress():判断是否是组播地址。IPv4中组播地址范围是224.0.0.0~239.255.255.255,IPv6中组播地址以FF开头。
  • isMCGlobal():判断是否是全球组播地址。全球组播地址可能在全世界都有订购者。IPv6中全球组播地址以FF0E或FF1E开头,取决于这个组播地址是已知的永久分配地址还是一个临时地址。IPv4中所有组播地址都是全球范围的,范围依靠TTL(Time To Live,生存时间)控制。
  • isMCOrgLocal():判断是否是组织范围组播地址。组织范围组播地址可能在公司或组织的所有网站都有订购者,但不包括组织外。以FF08或FF18开头,取决于是已知的永久分配地址还是临时地址。
  • isMCSiteLocal():判断是否是网站范围组播地址。发送到网站范围组播地址的包只会在本地网站内传输。以FF05或FF15开头,取决于是已知的永久分配地址还是临时地址。
  • isMCLinkLocal():判断是否是子网范围组播地址。发送到子网范围组播地址的包只会在子网内传输。以FF02或FF12开头,取决于是已知的永久分配地址还是临时地址。
  • isMCNodeLocal():判断是否是本地接口组播地址。发送到本地接口组播地址的包不能发送到最初的网络接口以外,即使是相同节点上的不同网络接口也不行,主要用于网络调试。以FF01或FF11开头,取决于是已知的永久分配地址还是临时地址。

测试可达性

InetAddress类还提供了两个isReachable()方法,用于测试节点对当前主机是否可达,原理是traceroute方法。

public boolean isReachable(int timeout) throws IOExceptionpublic boolean isReachable(NetworkInterface interface int ttl,int timeout) throws IOException

如果主机在timeout毫秒内响应则返回true,否则返回false。如果出现网络错误则抛出IOException异常。第二个方法允许指定从哪个本地网络接口建立连接,以及生存时间TTL。

Object方法

  • equals():只要两个InetAddress对象指向同一个IP地址就返回true,不考虑主机名。
  • hashcode():同样只根据IP地址计算。
  • toString():格式为主机名/IP地址,若主机名不存在则将主机名置空。

Inet4Address与Inet6Address

Inet4Address与Inet6Address是InetAddress的两个子类,用于区分IPv4地址和IPv6地址。一般来讲,编程人员处于应用层,不需要了解IP地址的细节,因此这两个子类很少用到。

NetworkInterface类

NetworkInterface类表示一个本地IP地址,可能是一个物理接口(额外的以太网卡),也可能是一个虚拟接口(与其他IP地址绑定到同一个物理硬件)。NetworkInterface提供了枚举本地地址的方法。

示例4:列出所有网络接口

public static void listAllNetworkInterfaces(){    try {        Enumeration<NetworkInterface> interfaces =                 NetworkInterface.getNetworkInterfaces();        while (interfaces.hasMoreElements()) {            NetworkInterface networkInterface =                    (NetworkInterface) interfaces.nextElement();            System.out.println(networkInterface);        }    } catch (SocketException e) {        e.printStackTrace();    }}

除此之外,NetworkInterface类还提供了两个静态工厂方法,用于根据名称或IP地址获取NetworkInterface对象:

public static NerworkInterface getByName(String name) throws SocketExceptionpublic static NetworkInterface getByInetAddress(InetAddress address) throws SocketException

获得了NetworkInterface对象后,就可以利用它的一系列get方法获取IP地址、名称等信息。

原创粉丝点击