前言
Java具有较好的网络编程模型/库,其中非常重要的一个API便是InetAddress。在在java.net网络编程中中有许多类都使用到了InetAddress,包括ServerSocket,Socket,DatagramSocket等等。
你要进行网络编程就得有IP地址、域名、主机等要素,而一个InetAddress里就保存着IP地址,同时还可能包含主机名,并且它提供了主机名 - IP地址互转的方法(比简单的域名解析还牛有木有),本来主要就介绍它看看能够怎么玩。
关于常用的网络概念,请提前做功课:一文搞懂常用的网络概念:域名、静态IP和动态IP、域名解析DNS、动态域名解析DDNS
正文
域名你不陌生,IP地址你也不陌生,但域名解析或许你有些陌生。Java并不希望使用者了解过多的DNS相关知识,因此使用了InetAddress来完成域名 - IP地址的互转工作。
Java域名解析
Java提供InetAddress类(有Inet4Address和Inet6Address两种实现),可以对域名-IP进行正向、逆向解析。InetAddress解析的时候一般是调用系统自带的DNS程序。
比如:Linux下默认使用哪个DNS去解析以及其规则是由/etc/resolv.conf该文件制定的(文件的内容如上示例内容)
对于有些域名,例如www.baidu.com,在不同地区拥有不同的IP;因此使用不同的DNS服务器进行解析,得到的IP一般也不一样。
简单的说:你所处的位置不同,解析www.baidu.com得到的IP地址也就是不一样的
InetAddress
IP地址是IP使用的32位(IPv4)或者128位(IPv6)位无符号数字,它是传输层协议TCP,UDP的基础。InetAddress是Java对IP地址的封装。
java.net.IntAddress类是Java对IP地址的高层表示。大多数其它网络类都要用到这个类,包括Socket、ServerSocket、URL、DatagramSocket、DatagramPacket等。InetAddress的实例对象包含了IP地址,同时还可能包含主机名(如果使用主机名来获取InetAddress的实例,或者使用数字来构造,并且启用了反向主机名解析的功能)。InetAddress类提供了将主机名解析为IP地址(或反之)的方法。
InetAddress对域名进行解析是使用本地机器配置(如域名系统DNS和网络信息服务(Network Information Service,NIS))来实现。本地需要向DNS服务器发送查询的请求,然后服务器根据一系列的操作,返回对应的IP地址,为了提高效率,通常本地会缓存一些主机名与IP地址的映射,这样访问相同的地址,就不需要重复发送DNS请求了。
在java.net.InetAddress类同样采用了这种策略。在默认情况下,会缓存一段有限时间的映射,对于主机名解析不成功的结果,会缓存非常短的时间(10秒)来提高性能和准确性。
静态方法得到InetAddress实例
InetAddress并没有提供public的构造器,而是提供了6个静态方法让你构造实例:
public static InetAddress[] getAllByName(String host); public static InetAddress getByName(String host); public static InetAddress getByAddress(String host, byte[] addr); public static InetAddress getByAddress(byte[] addr); public static InetAddress getLoopbackAddress(); public static InetAddress getLocalHost();
下面分别进行解释说明。
说明:一些的解析结果你可能和我不一样,因为即使对于同一个域名,在不同地方,设置不同时刻解析出来的IP也有可能是不一样的。
- public static InetAddress[] getAllByName(String host):给定主机名,返回其IP地址的数组,基于系统配置的DNS服务解析。当然host可以是主机名(域名)或或者是ip地址,这里以www.baidu.com为例。
@Test public void fun3() throws UnknownHostException { InetAddress[] inets = InetAddress.getAllByName("www.baidu.com"); for (InetAddress inet : inets) { // www.baidu.com/61.135.169.125 // www.baidu.com/61.135.169.121 System.out.println(inet); } }
public static InetAddress getByName(String host)
:它的原理是上面的方法 ->InetAddress.getAllByName(host)[0]
取值第一个就是它
@Test public void fun2() throws UnknownHostException { // 网络域名 InetAddress inet = InetAddress.getByName("www.baidu.com"); System.out.println("域名:" + inet.getHostName()); // 域名:www.baidu.com System.out.println("IP地址:" + inet.getHostAddress()); // IP地址:61.135.169.125 // 本地域名(本机) inet = InetAddress.getByName("localhost"); System.out.println("域名:" + inet.getHostName()); // 域名:localhost System.out.println("IP地址:" + inet.getHostAddress()); // IP地址:127.0.0.1 // 不存在的域名 抛出异常:java.net.UnknownHostException: aaaaaa.com // tips:abc.com这种域名是存在的哟 inet = InetAddress.getByName("aaaaaa.com"); System.out.println("域名:" + inet.getHostName()); System.out.println("IP地址:" + inet.getHostAddress()); }
对于此部分的域名解析,有如下注意事项:
- 对于外网域名的解析(如www.baidu.com),你的机器必须能够访问外网才能解析到IP地址。否则java.net.UnknownHostException
- 当然若你是在Linux下通过resolv.conf指定了自己的域名解析器,那么到底解析到哪去由你决定(比如你的内网域名都可以被解析了)
- 域名不能加上协议。若你这么写http://www.baidu.com就抛错UnknownHostException
- 对于外网域名解析,每个人解析得到的地址可能不一样。比如此处我对www.baidu.com解析得到的地址是61.135.169.125,是因为我在北京所以得到的是北京的一个IP地址
另外,为了方便你在windows里看到DNS缓存的效果,你可以使用这两个命令来查看:
- ipconfig /displaydns:展示出当前的dns本地缓存
- ipconfig /flushdns:清空本地缓存
以上两个方法也叫:用域名创建InetAddress对象。这种方式想获得IP的话,必须经过DNS服务解析~
但是请注意:如果你host传入的就是ip地址的话,就不会经过DNS解析了
public static InetAddress getByAddress(String host, byte[] addr)
:根据提供的主机名以及 IP 地址创建InetAddress
@Test public void fun0() throws UnknownHostException { // 同时指定域名 和 ip地址,那就是自己建立了对应关系喽 InetAddress inet = InetAddress.getByAddress("www.baidu.com", new byte[]{61, (byte) 135, (byte) 169, 125}); System.out.println("域名:" + inet.getHostName()); // 域名:www.baidu.com System.out.println("IP地址:" + inet.getHostAddress()); // IP地址:61.135.169.125 }