欢迎光临
我们一起进阶

Java网络编程(五):InetAddress类

扫码或搜索:沉默王二
发送 290992
即可立即永久解锁本站全部文章

本篇我们来学习Java网络编程的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.itmind.net");
}```

输出:
www.itmind.net/183.232.231.172

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

实例2:查找本地机器的地址
```java
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 UnknownHostException
public 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.ttlnetworkaddress.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.itmind.net构建的InetAddress对象调用getHostName()和getCanonicalHostName()的结果:

www.itmind.net
a23-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 IOException
public boolean isReachable(NetworkInterface interface int ttl,int timeout) throws IOException

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

赞(0) 打赏
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

小白学堂,学的不止是技术,更是前程

关于我们免责声明

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏