杰瑞科技汇

java的inetaddress

InetAddress 是 Java 网络编程中一个非常核心和基础的类,它不对应任何具体的网络协议,而是用于表示 IP 地址(无论是 IPv4 还是 IPv6),你可以把它想象成一个“网络地址的抽象表示”。

java的inetaddress-图1
(图片来源网络,侵删)

InetAddress 的核心概念

InetAddress 主要封装了两个关键信息:

  1. 主机名www.google.comlocalhost
  2. IP 地址217.160.142 (IPv4) 或 2607:f8b0:4006:805::200e (IPv6)。

它不包含端口号,端口号由另一个类 java.net.InetSocketAddress 来处理。

InetAddress 是一个抽象类,我们通常不直接使用 new 来创建它的实例,而是通过它提供的静态工厂方法来获取。


如何获取 InetAddress 实例?

主要有以下几种静态方法:

java的inetaddress-图2
(图片来源网络,侵删)

1 根据主机名获取

这是最常见的用法,通过一个易于记忆的主机名来获取其 IP 地址信息。

  • getByName(String host): 最常用的方法,它可以根据主机名获取 InetAddress 实例,如果主机名无法解析,会抛出 UnknownHostException 异常。
  • getAllByName(String host): 如果主机名对应多个 IP 地址(为了负载均衡或冗余),此方法会返回一个 InetAddress 数组。

示例代码:

import java.net.InetAddress;
import java.net.UnknownHostException;
public class GetByNameExample {
    public static void main(String[] args) {
        String host = "www.baidu.com";
        try {
            // 获取单个 InetAddress
            InetAddress address = InetAddress.getByName(host);
            System.out.println("主机名: " + address.getHostName());
            System.out.println("IP 地址: " + address.getHostAddress());
            System.out.println("完整地址字符串: " + address.toString());
            System.out.println("------------------------------------");
            // 获取所有 InetAddress
            InetAddress[] allAddresses = InetAddress.getAllByName(host);
            System.out.println(host + " 的所有 IP 地址:");
            for (InetAddress addr : allAddresses) {
                System.out.println(" - " + addr.getHostAddress());
            }
        } catch (UnknownHostException e) {
            System.err.println("无法找到主机: " + host);
            e.printStackTrace();
        }
    }
}

输出可能类似于:

主机名: www.baidu.com
IP 地址: 182.61.200.7
完整地址字符串: www.baidu.com/182.61.200.7
------------------------------------
www.baidu.com 的所有 IP 地址:
 - 182.61.200.7
 - 182.61.200.6
 - 182.61.200.5

2 根据IP地址获取

如果你已经知道 IP 地址,也可以通过它来获取 InetAddress 实例。

java的inetaddress-图3
(图片来源网络,侵删)
  • getByAddress(byte[] addr): 通过一个字节数组(IPv4 是 4 字节,IPv6 是 16 字节)来创建 InetAddress 实例。
  • getByAddress(String host, byte[] addr): 通过主机名和字节数组来创建,可以同时指定两者。

示例代码:

import java.net.InetAddress;
import java.net.UnknownHostException;
public class GetByAddressExample {
    public static void main(String[] args) {
        try {
            // 通过 IPv4 地址的字节数组创建
            byte[] ipv4Address = { (byte) 8, 8, 8, 8 }; // Google DNS
            InetAddress address = InetAddress.getByAddress(ipv4Address);
            System.out.println("IP 地址: " + address.getHostAddress());
            System.out.println("主机名: " + address.getHostName()); // 可能返回 IP 本身,如果无法反向解析
            System.out.println("------------------------------------");
            // 通过主机名和 IP 地址创建
            byte[] ipv4Address2 = { (byte) 114, 114, 114, 114 }; // 114 DNS
            InetAddress address2 = InetAddress.getByAddress("dns114", ipv4Address2);
            System.out.println("指定主机名: " + address2.getHostName());
            System.out.println("IP 地址: " + address2.getHostAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

3 获取本地地址

  • getLocalHost(): 获取本地主机的 InetAddress 实例,它尝试解析本地机器的主机名,如果解析失败,通常会返回一个以 0.0.1 (IPv4) 或 :1 (IPv6) 为地址的 InetAddress

示例代码:

import java.net.InetAddress;
import java.net.UnknownHostException;
public class GetLocalHostExample {
    public static void main(String[] args) {
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println("本地主机名: " + localHost.getHostName());
            System.out.println("本地IP地址: " + localHost.getHostAddress());
        } catch (UnknownHostException e) {
            System.err.println("无法获取本地主机信息。");
            e.printStackTrace();
        }
    }
}

InetAddress 的常用方法

获取到 InetAddress 实例后,你可以调用以下方法来获取信息:

方法 描述
getHostName() 获取此 IP 地址的主机名,如果无法通过反向 DNS 解析,可能会返回 IP 地址本身。
getHostAddress() 返回 IP 地址的字符串表示。"192.168.1.1""2001:db8::1"
getAddress() 返回 IP 地址的原始字节数组形式。
toString() 返回主机名和 IP 地址的字符串,格式为 hostname/address
isLoopbackAddress() 判断 IP 地址是否是环回地址,IPv4 是 0.0.1,IPv6 是 :1
isAnyLocalAddress() 判断 IP 地址是否是“通配符”地址,IPv4 是 0.0.0,IPv6 是 。
isLinkLocalAddress() 判断是否是链路本地地址(IPv6 的 fe80::/10)。
isSiteLocalAddress() 判断是否是站点本地地址(IPv6 的 fec0::/10,已废弃,但仍有实现)。
isMCGlobal() 判断是否是全球组播地址。
isMCLinkLocal() 判断是否是链路本地组播地址。
isMCSiteLocal() 判断是否是站点本地组播地址。
isMulticastAddress() 判断是否是组播地址。
isReachable(int timeout) 网络探测:尝试判断此地址是否可以到达,它会尝试建立一个 ICMP (ping) 请求或 TCP 连接。timeout 是超时时间(毫秒)。注意:此操作可能耗时且不总是可靠,因为它可能被防火墙阻止。

示例代码(使用 isReachable):

import java.net.InetAddress;
import java.net.UnknownHostException;
public class ReachableExample {
    public static void main(String[] args) {
        String host = "www.google.com";
        try {
            InetAddress address = InetAddress.getByName(host);
            System.out.println("正在尝试连接到 " + address.getHostAddress() + "...");
            // 设置超时时间为 5000 毫秒 (5秒)
            boolean isReachable = address.isReachable(5000);
            if (isReachable) {
                System.out.println("主机 " + host + " 可以到达。");
            } else {
                System.out.println("主机 " + host + " 无法到达。");
            }
        } catch (UnknownHostException e) {
            System.err.println("未知主机: " + host);
        } catch (Exception e) {
            System.err.println("连接测试时发生错误: " + e.getMessage());
        }
    }
}

IPv4 vs. IPv6

InetAddress 类天然支持 IPv4 和 IPv6,具体使用哪种,取决于以下几个因素:

  1. 操作系统和 JVM 配置:现代操作系统和 JVM 默认都支持 IPv6。
  2. DNS 解析结果:DNS 服务器返回的记录决定了 getByName 返回的是 Inet4Address 还是 Inet6Address
  3. java.net 系统属性:你可以通过 JVM 参数来控制网络栈的行为。

相关的系统属性:

属性名 描述 默认值
java.net.preferIPv4Stack 如果设置为 true,则禁用 IPv6,只使用 IPv4。 false
java.net.preferIPv6Addresses 如果设置为 true,在主机名解析时,优先返回 IPv6 地址,如果为 false,则优先返回 IPv4 地址。 false

示例: 在运行程序时,强制使用 IPv4 栈:

java -Djava.net.preferIPv4Stack=true YourClassName

最佳实践和注意事项

  1. 总是处理异常:所有涉及主机名解析的方法都可能抛出 UnknownHostException,务必使用 try-catch 块处理。
  2. 不要阻塞主线程getByNameisReachable 都是网络 I/O 操作,可能会阻塞较长时间,在 GUI 应用或高并发服务器中,应在后台线程(如 ExecutorService)中执行这些操作。
  3. isReachable 的局限性:不要过度依赖 isReachable 的结果,防火墙、NAT 设备、主机配置等都可能导致 false 的误报,对于关键的网络连通性检查,最好尝试建立你自己的 TCP 连接(连接到目标主机的 80 端口)。
  4. 缓存结果:DNS 解析是一个相对昂贵的操作,如果你的应用需要频繁地查询同一个主机名,考虑将解析结果缓存起来,以减少网络开销和延迟。
  5. 理解 getHostName():调用 getHostName() 可能会触发一次反向 DNS 查询,这同样是一个网络操作,也可能耗时,如果你只需要 IP 地址,直接使用 getHostAddress() 更高效。

InetAddress 是 Java 网络编程的基石,它将复杂的 IP 地址和主机名概念封装成了一个易于使用的对象,掌握如何创建、获取和使用 InetAddress 实例,是学习更高级网络知识(如 Socket 编程、HTTP 请求等)的必备前提,记住它的核心功能——地址解析,并注意其可能带来的网络 I/O 阻塞和异常问题。

分享:
扫描分享到社交APP
上一篇
下一篇