加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

网络编程-Java中的Internet查询

发布时间:2020-12-15 06:39:22 所属栏目:Java 来源:网络整理
导读:前提 在深入理解URL、URI等概念,或者学些Socket相关的知识之,有必要系统理解一下Internet相关的一些基础知识。 Internet地址 连接到Internet(因特网)的设备称为节点(node),而任意一个计算机节点称为主机(host)。每个节点或者主机都由至少一个唯一的数来标

前提

在深入理解URL、URI等概念,或者学些Socket相关的知识之,有必要系统理解一下Internet相关的一些基础知识。

Internet地址

连接到Internet(因特网)的设备称为节点(node),而任意一个计算机节点称为主机(host)。每个节点或者主机都由至少一个唯一的数来标识,这称为Internet地址或者IP地址。

IP和域名

如果使用Java作为开发语言的话,不需要担心IP或者域名的工作原理,但是我们需要理解IP寻址的一些基础知识。我们目前常用的网络都是IPv4网络,每个计算机节点都是由一个4字节(32bit)的数字标识,这个数字标识的格式是点分四段(dotted quad,形式是:xxx.xxx.xx.xx),其中的每个数字都是一个无符号字节,取值范围是0到255。当数据通过网络传输的时候,数据包的首部中要包括要发往的机器地址(目的地址)和发送这个数据包的机器地址(源地址)。

可以使用的IPv4类型的IP地址总量大概是40亿多一点,因此无法做到地球上每个人都分配一个唯一的IPv4的IP地址,所以IPv6就诞生了,目前网络由IPv4向IPv6过度(不过这个过度过程相对缓慢,因素很多)。IPv6网络中的IP地址使用16字节(128bit)的数字标识。IPv6地址的表示形式通常是以英文冒号分隔的8个区域,每个区域都是4个十六进制的数字,举个例子:FEDC:BA98:7654:3210:FEDC:BA98:7654:3210就是一个合法的IPV6地址。而在IPv4和IPv6的混合网络中,IPv6地址的最后四个字节有时候表示形式为IPv4地址的点分四段地址。IPv6地址只在Jdk1.4以及之后的版本支持,换言之,Jdk1.3或者之前的版本只能使用IPv4地址。

虽然计算机可以轻松地处理数字,但是人脑的记忆对于数字并不敏感,因此开发了域名系统(Domain Name System,也就是DNS),用于将人脑易于记忆的主机名(如www.baidu.com)转换为数字Internet地址(如183.232.231.173)。这里不展开DNS的具体内容,作为开发者,我们可以简单理解为它就是一个巨型分布式数据库,用于映射主机名(域名)和IP地址,画个图大致如下:

j-i-q-1

端口

因为每台计算机都不是只做一件事,相当于计算机做的每一种业务逻辑需要从逻辑上隔离,例如FTP请求的处理要和电子邮件的处理分离,FTP请求处理也要和Web业务处理分离,所以每种业务逻辑的处理需要使用一个逻辑分离的标识,这个标识就是端口(port)。一般每台计算机有成千上万个逻辑端口(确切来说,每个传输层协议都有65535个端口,Windows系统中,11023号端口是系统端口,用户无法修改,102465534端口是系统为用户预留的端口,而65535号端口为系统保留端口),每个端口可以分配给一个特定的服务。例如Web的底层协议Http协议通讯一般使用80端口,使用浏览器URL访问服务器的80端口可以省略URL中的端口号。

Java对网络的抽象

InetAddress

单词InetAddress是Internet Address的缩写合并,代表因特网地址。java.net.InetAddress类是Java对IP地址(包括IPv4和IPv6地址)的高度抽象表示。大多数网络编程相关的类都会用到InetAddress,如Socket、ServerSocket等,InetAddress两个最核心的属性是主机名(host)和IP地址,对应属性hostName和address。

创建InetAddress实例

创建InetAddress实例主要依赖它的工厂方法(实际上InetAddress的构造函数是包私有的,也就是无法通过new关键字创建实例),比较常用的一个静态工厂方法是:

static InetAddress getByName(String host) throws UnknownHostException

其中参数可以为主机名(域名)或者点分四段地址,前者相当于通过主机名查找一个可连接的IP地址,后者相当于通过IP地址反查主机名,值得注意的是,这个方法调用的使用会建立与本地DNS服务器的连接进行主机名或者数字地址查找,如果DNS服务器找不到主机或者地址,会抛出UnknownHostException异常。

	public static void main(String[] args) throws Exception{
		InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
		System.out.println(inetAddress);
	}

InetAddress覆写了toString方法,返回结果是hostName/address格式,上面的main方法执行的一个可能的结果是:

www.baidu.com/14.215.177.39

有些时候,我们知道数字IP地址,就可以由数字地址直接创建一个InetAddress实例,这样就可以不必使用getByName(String host)方法和DNS交互。

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

这两个方法可以创建主机名不存在或者主机名无法解析的InetAddress实例。举个例子:

	public static void main(String[] args) throws Exception {
		byte[] bytes = {14,(byte) 215,(byte) 177,39};
		InetAddress inetAddress = InetAddress.getByAddress("www.doge.com",bytes);
		System.out.println(inetAddress);
	}

实际上,域名www.doge.com并不存在,但是这个方法并不会抛出异常。

如果要查询一个主机名的所有IP地址,可以使用:

static InetAddress[] getAllByName(String host) throws UnknownHostException

如果需要查询本机的主机名和IP地址,可以使用:

static InetAddress getLocalHost() throws UnknownHostException

注意这个方法会尝试连接DNS去查询本地计算机的真正的主机名和IP地址,如果查询失败,它就会返回回送地址,也就是主机名是"localhost",IP地址是点分四段地址"127.0.0.1"。

InetAddress缓存

DNS查找的开销可能相当大(如果请求需要经过多个中间服务器或者尝试解析一个不可达的主机,可能需要耗费几秒的时间),所以InetAddress会缓存DNS查询结果,也就是一旦得到一个给定主机的地址,就不会再次查找,即使为同一个主机创建多个InetAddress实例,也不会再次进行DNS查询。这样的缓存机制对于性能来说是有好处的,但是也会带来负面影响:

  • 程序运行期间连接的主机的IP地址很大可能会发生变化,已缓存的IP有可能不可用。
  • 刚开始尝试解析一个主机时候是失败的,但是随后尝试解析的时候会成功,但是缓存了首次解析失败的记录。
  • 远程DNS服务器发送的信息还在传输,第一次尝试超时,下一次请求即可成功。

因此,Java对于不成功的DNS查询结果仅仅缓存10秒,而且可以通过下面两个系统变量控制缓存的时间:

  • networkaddress.cache.ttl:成功的DNS查询结果的缓存时间(秒数),-1表示不会超时。
  • networkaddress.cache.negative.ttl:成功的DNS查询结果的缓存时间(秒数),-1表示不会超时。

InetAddress提供的基本属性获取方法

InetAddress提供四个基本属性获取方法,用于获取当前InetAddress表示的主机名和IP地址。

public String getHostName();
public String getCanonicalHostName();
public byte[] getAddress();
public String getHostAddress();

注意上面的几个方法只有Getter,没有Setter方法,说明这几个属性的设置权限是java.net包中的类库。

  • getHostName:返回当前InetAddress实例的主机名,如果对应的机器没有主机名或者安全管理器阻止确定主机名,则会返回点分四段数字IP地址。
  • getCanonicalHostName:getCanonicalHostName与getHostName,不过getHostName方法只是在不知道主机名的情况下才连接DNS进行查询,getCanonicalHostName方法总是连接DNS查询主机名并且替换缓存值,所以这个方法调用会比较耗时。
  • getAddress:返回当前InetAddress实例的数字IP地址的byte数组,注意因为Java中没有无符号的byte,因此负数byte值要+256变成int类型才是无符号的byte值。
  • getHostAddress:实际上就返回getAddress方法中的byte数组转换成的IP地址点分四段表示形式的字符串,也就是IP地址字符串。

上面的getAddress()方法还有一个特殊的判断使用场景,就是它的返回值byte数组的长度如果是4,那么InetAddress一定是Inet4Address的实例,如果长度为16,那么那么InetAddress一定是Inet6Address的实例,由此可以判断InetAddress中的IP地址到底是IPv4还是IPv6。

InetAddress提供的地址类型判断方法

有些IP地址和地址模式有特殊的含义,例如127.0.0.1是本地回送地址,244.0.0.0到239.255.255.255范围内的IPv4地址是组播地址。InetAddress中提供10个公有实例方法来判断InetAddress对象是否符合这些地址模式:

  • 1、boolean isAnyLocalAddress():如果地址是通配地址则返回true,所谓通配地址就是可以匹配本地系统中的任何地址,在IPv4中的通配地址是0.0.0.0,在IPv6中的通配地址是0:0:0:0:0:0:0:0(:

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读