购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.1 IP地址类InetAddress

InetAddress类表示互联网协议(IP)地址。IP地址是IP使用的32位(IPv4)或者128位(IPv6)无符号数字,它是传输层协议TCP、UDP的基础。InetAddress是Java对IP地址的封装,几乎所有的Java网络相关的类都和它有关系,例如serversocket、socket、URL、DatagramSocket、DatagramPacket等。首先我们温习一下几种常见类型的IPv4地址。

InetAddress的实例对象包含以数字形式保存的IP地址,同时还可能包含主机名(使用主机名来获取InetAddress的实例,或者使用数字来构造,并且启用了反向主机名解析的功能)。InetAddress类提供了将主机名解析为IP地址(或将IP地址解析为主机名)的方法。

InetAddress对域名进行解析是使用本地机器配置或者网络命名服务(如域名系统(Domain Name System,DNS)和网络信息服务(Network Information Service,NIS))来实现的。对于DNS来说,本地需要向DNS服务器发送查询的请求,然后服务器根据一系列的操作返回对应的IP地址。为了提高效率,通常本地会缓存一些主机名与IP地址的映射,这样访问相同的地址就不需要重复发送DNS请求了。java.net.InetAddress类同样采用了这种策略。在默认情况下,会缓存一段有限时间的映射,对于主机名解析不成功的结果,会缓存非常短的时间(10秒)来提高性能。

Java提供了InetAddress类来代表IP地址,InetAddress下还有两个子类:Inet4Address和Inet6Address,它们分别代表IPv4地址和IPv6地址,不过这两个子类不常用,这里不再赘述。

InetAddress类定义在Java.net包中,该类表示网络接口地址。对于IPv4地址,是指IP地址、子网掩码和广播地址。对于IPv6地址,是指IP地址和网络前缀长度。InetAddress类没有构造方法(即构造器),所以不能直接创建出一个对象,可以通过InetAddress类提供的一些静态方法返回InetAddress的对象(即实例),比如获得本地主机的地址:

InetAddress address = InetAddress.getLocalHost(); //返回InetAddress对象
System.out.println("本主机名:" + address.getHostName()); //打印主机名

InetAddress类提供的常用方法如下:

byte[] getAddress()

返回此InetAddress对象的原始IP地址。

static InetAddress[] getAllByName(String host)

给定主机的名称,根据系统上配置的名称服务返回其IP地址数组。

static InetAddress getByAddress(byte[] addr)

根据原始IP地址得到InetAddress对象。

static InetAddress getByAddress(String host, byte[] addr)

根据提供的主机名和IP地址创建并返回InetAddress。

static InetAddress getByName(String host)

根据提供的主机名来创建并返回InetAddress。

String getCanonicalHostName()

获取此IP地址的完全限定域名。

String getHostAddress()

返回文本显示中的IP地址字符串。

String getHostName()

获取此IP地址的主机名。

static InetAddress  getLocalHost()

返回本地主机的地址。

static InetAddress  getLoopbackAddress()

返回本地环回地址。

int hashCode()

返回此IP地址的哈希码。

boolean isAnyLocalAddress()

检查通配符地址中的InetAddress的实用程序。

boolean isLinkLocalAddress()

检查InetAddress是不是链接本地地址的实用程序。

boolean isLoopbackAddress()

检查InetAddress是不是一个本地环回地址的实用程序。

boolean isMCGlobal()

检查多播地址是否具有全局范围的实用程序。

boolean isMCLinkLocal()

检查组播地址是否具有链路范围的实用程序。

boolean isMCNodeLocal()

检查多播地址是否具有节点范围的实用程序。

boolean isMCOrgLocal()

检查组播地址是否具有组织范围的实用程序。

boolean  isMCSiteLocal()

检查多播地址是否具有站点范围的实用程序。

boolean  isMulticastAddress()

检查InetAddress是不是IP组播地址的实用程序。

boolean  isReachable(int timeout)

根据指定时间测试网络是否可达,是否可以建立连接。

boolean  isReachable(NetworkInterface netif, int ttl, int timeout)

根据本机特定的网卡、生存时间和指定时间来测试网络是否可达。

boolean  isSiteLocalAddress()

检查InetAddress是不是站点本地地址的实用程序。

String  toString()

将此IP地址转换为String类型。

4.1.1 获取远程Web主机的IP地址

远程Web主机也就是互联网上的网站所在的服务器主机。我们可以通过getByName方法来获得Web主机的IP地址,只需要把域名(比如www.qq.com)作为参数传入getByName中,然后返回InetAddress对象,最后通过getHostAddress得到字符串形式的IP地址。getByName方法根据提供的主机名来创建并返回InetAddress,该方法的声明如下:

static InetAddress getByName(String host)

其中参数host表示指定的主机名或者null。主机名可以是机器名,例如java.sun.com,或其IP地址的文本表示。如果提供了文字IP地址,则只会检查地址格式的有效性。如果传入的是null,则返回一个环回接口地址的InetAddress。该方法返回一个InetAddress对象。

下面乘热打铁,开启我们的第一个实例,获取腾讯网的Web主机的IP地址。

【例4.1】 获取远程Web主机的IP地址

1)打开Eclipse,新建一个Java工程,工程名是myprj。在工程中新建一个类,类名是test。然后在test.java中输入如下代码:

package myprj;
import java.net.*;
public class test {
    public static void main(String[] args) {
        //TODO Auto-generated method stub
         try{ //以下代码通过域名创建InetAddress对象
              InetAddress addr = InetAddress.getByName("www.qq.com");
              String domainName = addr.getHostName();     //获得主机名
              String IPName = addr.getHostAddress();      //获得IP地址
              System.out.println(domainName);
              System.out.println(IPName);
            }catch(UnknownHostException e){
              e.printStackTrace();
            }
    }
}

在上述代码中,首先通过InetAddress类的静态方法getByName来获取域名为www.qq.com的InetAddress对象,然后通过getHostName方法获取此IP地址的主机名(因为是Web服务器主机,所以主机名通常就是域名,也就是www.qq.com),再通过getHostAddress方法获得IP地址,最后分别打印主机名和IP地址。

2)保存工程并运行,运行结果如下:

www.qq.com
101.91.42.232

至此,已成功获取远程主机的IP地址。现在我们回到本地来获取本地计算机的主机名和IP地址。

4.1.2 获取本地环回地址

为了标识和管理网络设备(如路由器、交换机、服务器或PC等),我们通常会在这些设备的接口(包括物理接口和逻辑接口,如VLAN)上设置IP地址。很多情况下,尽管该设备未脱离网络,由于其管理地址所处的接口状态为禁用(比如禁用某个网卡),该设备便无法管理。为了解决这个问题,于是就出现了环回接口。该接口是设备上的一个逻辑接口,该接口的状态不受物理端口启用/禁用的影响,只要设备的系统协议不出问题,该接口就不会禁用掉。顺便补充一下,尽管3层VLAN也是逻辑接口,但通常我们使用的VLAN都是基于端口的,而且核心层交换机VLAN一般只关联一个端口,当端口状态处于禁用时,VLAN接口是无法启用的。

由此可见,环回接口的地址无疑是标识网络设备本身的最佳选择,因为只要设备运行正常,它将永远处于up状态。

本地环回地址即环回接口上设置的地址,该地址用于标识设备本身。A类地址段127.0.0.0被用作本地环回地址,一般设备都默认采用127.0.0.1,当然也可以在环回接口上设置公网IP,作为全网的设备标识。A类网络127就是为环回接口预留的。根据惯例,大多数系统把IP地址127.0.0.1分配给这个接口,并命名为localhost。一个传给环回接口的IP数据报不能在任何网络上出现。实际上,访问127.x.x.x的所有IP都是访问环回接口。当设备给其自身发数据报时,是把该数据报送往其环回接口(其实是直接送给CPU处理)。

总之,127.0.0.1通常被称为本地环回地址,不是一个物理接口上的地址。它代表设备的本地虚拟接口,所以默认被看作永远不会宕掉的接口。虽然很多主机的默认本地环回地址都是127.0.0.1,但也不是完全绝对的,我们可以通过调用getByName函数来获得主机的环回接口的地址,只要传null给getByName即可,也可以通过调用getLoopbackAddress函数来获得。

【例4.2】 得到环回接口的名称和IP地址

1)打开Eclipse,新建一个Java工程,工程名是myprj。在工程中新建一个类,类名是test。然后在test.java中输入如下代码:

package myprj;
import java.net.InetAddress;
import java.io.IOException;

public class test {
    public static void main(String[] args)throws IOException {
        InetAddress address = InetAddress.getByName(null);
         System.out.println("方法一:环回接口名称和地址:" + address);
         InetAddress address2 = InetAddress.getLoopbackAddress();
         System.out.println("方法二:环回接口名称和地址: " + address2);
    }
}

在上述代码中,我们直接调用InetAddress类的静态方法getByName来创建一个InetAddress对象,注意传入的参数是null,然后打印出环回接口的名称和地址,打印时address对象直接放在println中。

2)保存工程并运行,运行结果如下:

方法一:环回接口名称和地址:localhost/127.0.0.1
方法二:环回接口名称和地址:localhost/127.0.0.1

4.1.3 单网卡下的本机地址

这里所说的本地主机地址就是上网的网卡的IP地址(通常是上网路由器分配的地址)。调用getLocalHost方法可以获得单网卡情况下本地主机的物理网卡的地址信息。要注意的是,在主机只有一个网卡的情况下,在多网卡下不一定准确,比如安装了VMware虚拟机软件,就会多两个虚拟网卡,此时getLocalHost得到的可能是虚拟网卡的地址信息。这是为什么呢?这是因为getLocalHost会枚举本地所有网卡,并返回第一个合法的IP地址作为本地地址,如果本机只有一个网卡,则返回的第一个合法IP地址肯定就是该网卡的地址,所以没有问题。另外,getLocalHost的原理是对本机的主机名进行解析,从而获取IP地址。那么问题来了,如果在本机的/etc/hosts文件(/etc/hosts是Linux系统下的文件,Windows系统也有类似的hosts文件)中对这个主机名指向了一个错误的IP地址,那么InetAddress.getLocalHost就会返回这个错误的IP地址。当然,如果你的主机名是到DNS去解析的,碰巧DNS上的信息也是错的,那么同样是悲惨结局,这些错误情况在多网卡情况下很有可能会发生,但通常单网卡情况下不大会出错。

InetAddress.getLocalHost尽量在单网卡情况下使用,不要因为在多网卡主机情况下能正确获得IP地址而骄傲,一旦换了一台主机或许情况就不同了,因为这个方法可能会被hosts文件和DNS误导,显然这两个地方都不是本机IP地址的权威获取处,权威获取处是网卡本身的配置信息。

【例4.3】 获取单网卡下本地主机的地址信息

1)打开Eclipse,新建一个Java工程,工程名是myprj。在工程中新建一个类,类名是test。然后在test.java中输入如下代码:

package myprj;
import java.net.*;
import java.util.Arrays;
//获取本机的InetAddress实例
public class test {
    public static void main(String[] args) {
        try{
        InetAddress address = InetAddress.getLocalHost();    //得到本机地址
        System.out.println("计算机名:" + address.getHostName());
        System.out.println("IP地址:" + address.getHostAddress());
        byte[] bytes = address.getAddress();    //获取字节数组形式的IP地址
        System.out.println("字节数组形式的IP" +Arrays.toString(bytes));
        System.out.println(address);            //直接输出InetAddress对象

         for (byte ipSegment : bytes)
             System.out.print(ipSegment + " ");
         System.out.println("");
         for (byte ipSegment : bytes)
         {
             int newIPSegment = (ipSegment < 0) ? 256 + ipSegment : ipSegment;
             System.out.print(newIPSegment + " ");
         }
         System.out.println("");
         for (byte ipSegment : bytes)
         {
             int newIPSegment = (ipSegment < 0) ? 256 + ipSegment : ipSegment;
             System.out.print(Integer.toHexString(newIPSegment) + " ");
         }
         }catch(UnknownHostException e){
              e.printStackTrace();
         }
    }
}

getAddress这个方法返回的byte数组是有符号的。在Java中,byte类型的取值范围是-128~127。如果返回的IP地址的某个字节是大于127的整数,在byte数组中就是负数。由于Java中没有无符号byte类型,因此要想显示正常的IP地址,就必须使用int或long类型。这句代码演示了如何调用getAddress方法返回IP地址,以及如何将IP地址转换成正整数形式:

int newIPSegment = (ipSegment < 0) ? 256 + ipSegment : ipSegment;

在最后一个for循环中,我们通过Integer.toHexString()方法将整数转换为十六进制字符串的表示形式。

2)保存工程并运行,运行结果如下:

计算机名:WIN-K3T300RT59J
IP地址:192.168.1.2
字节数组形式的IP[-64, -88, 1, 2]
WIN-K3T300RT59J/192.168.1.2
-64 -88 1 2
192 168 1 2
c0 a8 1 2

从上面的运行结果可以看出,倒数第3行输出了未转换的IP地址,由于本机IP地址的第一个字节和第二字节均大于127,因此输出了一个负数。而倒数第二行由于将IP地址的每一个字节转换成了int类型,因此输出了正常的IP地址。最后一行将IP地址的每一个字节转换为十六进制形式,很多程序员喜欢以十六进制形式查看字节数组。

值得注意的是,本例虽然能得到本机的上网IP地址,但是仅仅针对简单网络的情况,如果是复杂网络的情况,则得到的IP地址不一定正确,此时需要用到网络接口类NetworkInterface。 7yNF6habuRs6GKVlRmfnxDCeGx5R5JfkWwBwr/9oGvUvDCN4XWBVsnZBTvFJ2OGm

点击中间区域
呼出菜单
上一章
目录
下一章
×