网络编程-Java中的Internet查询
2021-07-08 17:06
标签:set 因此 http协议 anon 感知 机制 数字 dock 版本 在深入理解URL、URI等概念,或者学些Socket相关的知识之,有必要系统理解一下Internet相关的一些基础知识。 连接到Internet(因特网)的设备称为节点(node),而任意一个计算机节点称为主机(host)。每个节点或者主机都由至少一个唯一的数来标识,这称为Internet地址或者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地址,画个图大致如下: 因为每台计算机都不是只做一件事,相当于计算机做的每一种业务逻辑需要从逻辑上隔离,例如FTP请求的处理要和电子邮件的处理分离,FTP请求处理也要和Web业务处理分离,所以每种业务逻辑的处理需要使用一个逻辑分离的标识,这个标识就是端口(port)。一般每台计算机有成千上万个逻辑端口(确切来说,每个传输层协议都有65535个端口,Windows系统中,1~1023号端口是系统端口,用户无法修改,1024~65534端口是系统为用户预留的端口,而65535号端口为系统保留端口),每个端口可以分配给一个特定的服务。例如Web的底层协议Http协议通讯一般使用80端口,使用浏览器URL访问服务器的80端口可以省略URL中的端口号。 单词InetAddress是Internet Address的缩写合并,代表因特网地址。java.net.InetAddress类是Java对IP地址(包括IPv4和IPv6地址)的高度抽象表示。大多数网络编程相关的类都会用到InetAddress,如Socket、ServerSocket等,InetAddress两个最核心的属性是主机名(host)和IP地址,对应属性hostName和address。 创建InetAddress实例主要依赖它的工厂方法(实际上InetAddress的构造函数是包私有的,也就是无法通过new关键字创建实例),比较常用的一个静态工厂方法是: 其中参数可以为主机名(域名)或者点分四段地址,前者相当于通过主机名查找一个可连接的IP地址,后者相当于通过IP地址反查主机名,值得注意的是,这个方法调用的使用会建立与本地DNS服务器的连接进行主机名或者数字地址查找,如果DNS服务器找不到主机或者地址,会抛出UnknownHostException异常。 InetAddress覆写了 有些时候,我们知道数字IP地址,就可以由数字地址直接创建一个InetAddress实例,这样就可以不必使用 这两个方法可以创建主机名不存在或者主机名无法解析的InetAddress实例。举个例子: 实际上,域名www.doge.com并不存在,但是这个方法并不会抛出异常。 如果要查询一个主机名的所有IP地址,可以使用: 如果需要查询本机的主机名和IP地址,可以使用: 注意这个方法会尝试连接DNS去查询本地计算机的真正的主机名和IP地址,如果查询失败,它就会返回回送地址,也就是主机名是"localhost",IP地址是点分四段地址"127.0.0.1"。 DNS查找的开销可能相当大(如果请求需要经过多个中间服务器或者尝试解析一个不可达的主机,可能需要耗费几秒的时间),所以InetAddress会缓存DNS查询结果,也就是一旦得到一个给定主机的地址,就不会再次查找,即使为同一个主机创建多个InetAddress实例,也不会再次进行DNS查询。这样的缓存机制对于性能来说是有好处的,但是也会带来负面影响: 因此,Java对于不成功的DNS查询结果仅仅缓存10秒,而且可以通过下面两个系统变量控制缓存的时间: InetAddress提供四个基本属性获取方法,用于获取当前InetAddress表示的主机名和IP地址。 注意上面的几个方法只有Getter,没有Setter方法,说明这几个属性的设置权限是java.net包中的类库。 上面的 有些IP地址和地址模式有特殊的含义,例如127.0.0.1是本地回送地址,244.0.0.0到239.255.255.255范围内的IPv4地址是组播地址。InetAddress中提供10个公有实例方法来判断InetAddress对象是否符合这些地址模式: 实际上,我们很少用到这十个方法。 InetAddress提供两个 这两个方法会尝试使用Traceroute查看指定地址是否可达。Traceroute程序使用ICMP报文和IP首部中的TTL字段(一般为64)。TTL字段的目的是防止数据报在选路时候无休止的在网络中流动(当路由故障的时候,可能在两个路由循环)。可以理解TTL字段用于控制连接被丢弃之前的网络最大跳数。第一个方法 InetAddress中覆盖了 上面的main方法执行之后,基本上打印true,取决于DNS的处理。 Inet4Address和Inet6Address两个类都继承自InetAddress,它们分别是IPv4和IPv6的IP地址的高度抽象表示。但是,一般情况下,开发者使无须感知使用或者连接的IP地址到底是IPv4和IPv6的IP地址,因为通常我们都是通过主机名(host)去访问。 NetworkInterface是本地网络接口,实际上它可以表示一个物理接口,如以太网卡,它也可以表示一个虚拟接口,与机器的其他IP地址绑定到同一个物理硬件,最常见的就是现在的虚拟化容器如Docker提供的网卡。NetworkInterface类提供的一些方法可以枚举所有的本地地址,通过这些本地地址创建的InetAddress对象,创建出来的这些InetAddress对象就可以使用在客户端Socket或者服务端Socket。 另外,可以通过InetAddress返回指定IP绑定的网络接口(或者说返回的网络接口处理指定的IP地址),如果本地主机没有网络接口和传入的IP地址绑定则返回null,如果发生错误则抛出一个SocketException。 最后,可以使用下面的方法枚举本地主机上的所有网络接口。 笔者用的是Windows10系统,尝试枚举一下所有的网络接口: 执行结果部分如下: NetworkInterface提供一个实例方法 实例方法 实例方法 只有理解网络编程中的一些基础概念,才能铺好学习URI(URL)、TCP协议、HTTP协议和套接字(Socket)的路。 (本文完 c-2-d) 网络编程-Java中的Internet查询 标签:set 因此 http协议 anon 感知 机制 数字 dock 版本 原文地址:https://www.cnblogs.com/throwable/p/9581785.html前提
Internet地址
IP和域名
端口
Java对网络的抽象
InetAddress
创建InetAddress实例
static InetAddress getByName(String host) throws UnknownHostException
public static void main(String[] args) throws Exception{
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress);
}
toString
方法,返回结果是hostName/address格式,上面的main方法执行的一个可能的结果是:www.baidu.com/14.215.177.39
getByName(String host)
方法和DNS交互。static InetAddress getByAddress(byte[] addr)throws UnknownHostException
static InetAddress getByAddress(String host,byte[] addr)throws UnknownHostException
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);
}
static InetAddress[] getAllByName(String host) throws UnknownHostException
static InetAddress getLocalHost() throws UnknownHostException
InetAddress缓存
InetAddress提供的基本属性获取方法
public String getHostName();
public String getCanonicalHostName();
public byte[] getAddress();
public String getHostAddress();
getAddress()
方法还有一个特殊的判断使用场景,就是它的返回值byte数组的长度如果是4,那么InetAddress一定是Inet4Address的实例,如果长度为16,那么那么InetAddress一定是Inet6Address的实例,由此可以判断InetAddress中的IP地址到底是IPv4还是IPv6。InetAddress提供的地址类型判断方法
boolean isAnyLocalAddress()
:如果地址是通配地址则返回true,所谓通配地址就是可以匹配本地系统中的任何地址,在IPv4中的通配地址是0.0.0.0,在IPv6中的通配地址是0:0:0:0:0:0:0:0(::)。boolean isLoopbackAddress()
:如果地址是回送地址则返回true,在IPv4中的回送地址是127.0.0.1,在IPv6中的回送地址是0:0:0:0:0:0:0:1(::1)。boolean isLinkLocalAddress()
:如果地址是一个IPv6本地链接地址则返回true。boolean isSiteLocalAddress()
:如果地址是一个IPv6本地网站地址则返回true。boolean isMulticastAddress()
:如果地址是一个组播地址则返回true。boolean isMCGlobal()
:如果地址是一个全球组播地址则返回true。boolean isMCOrgLocal()
:如果地址是一个组织范围组播地址则返回true。boolean isMCSiteLocal()
:如果地址是一个网站范围组播地址则返回true。boolean isMCLinkLocal()
:如果地址是一个子网范围组播地址则返回true。boolean isMCNodeLocal()
:如果地址是一个本地接口组播地址则返回true。InetAddress的可达性测试
isReaachable()
方法用于测试可达性。其实就是测试一个特定的节点对当前主机是否可达(两者是否能够建立一个网络连接)。因为网络连接有可能因为多种原因阻塞,列举一些原因如下:
public boolean isReachable(int timeout) throws IOException
public boolean isReachable(NetworkInterface netif, int ttl, int timeout) throws IOException
isReachable(int timeout)
只有一个参数控制检测可达性的超时毫秒数,第二个方法可以控制指定本地的网络接口、TTL参数和超时时间进行可达性测试。 public static void main(String[] args) throws Exception{
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress.isReachable(2000));
}
比较不同的InetAddress
equals
和hashCode
方法,比较的时候,实际上比较的是address属性,也就是IP地址,换言之,只要两个InetAddress的IP地址一致,这两个InetAddress对象就是相等的,并不要求两个InetAddress对象的主机名一致。举个例子: public static void main(String[] args) throws Exception {
while (true){
Thread.sleep(500);
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
InetAddress other = InetAddress.getByName("14.215.177.39");
System.out.println(inetAddress.equals(other));
}
}
Inet4Address和Inet6Address
本地网络接口
NetworkInterface的创建方法
static NetworkInterface getByName(String name) throws SocketException
getByName(String name)
方法可以指定网络接口名字获取对应的网络接口实例NetworkInterface,如果没有这样名字的网络接口则返回null。网络接口的名字格式和平台相关,例如典型的Unix系统上,以太网接口名字的形式为eth0、eth1等等,在Windows系统中名字类似于"CE31"、"ELX100"等字符串,"lo"一般是本地回送地址的网络接口名字。static NetworkInterface getByInetAddress(InetAddress addr) throws SocketException
static Enumeration
public static void main(String[] args) throws Exception{
Enumeration
name:lo (Software Loopback Interface 1)
name:ppp0 (WAN Miniport (PPPOE))
name:net0 (Microsoft ISATAP Adapter #2)
name:net1 (Microsoft ISATAP Adapter)
name:net2 (WAN Miniport (L2TP))
name:net3 (WAN Miniport (IKEv2))
...
NetworkInterface提供的属性获取方法
public Enumeration
用于获取绑定在一个网络接口上面的所有IP地址,虽然这种情况不常见,但是确实存在。public String getName()
返回NetworkInterface实例的对象名称,例如eth0、lo等。public String getDsiplayName()
也是返回NetworkInterface实例的对象名称,不过表示的形式更加友好,例如"Ethernet Card 0"(eth0),但是在Unix系统中和getName()
相同。小结