目录
前言
1 什么是套接字?
2 Java.net包
一、InetAddress类
1.1 创建InetAddress对象
1.2 判断IP类型
1.3 地址类型
1.4 缓存
1.5 测试可达性
二、NetworkInterface类
2.1 获取NetworkInterface对象
2.2 获取方法
前言
Java程序可以非常方便地访问互联网上的HTTP服务、FTP服务等,能够处理各种网络资源和网络通信,完成各种复杂的网络应用开发。网络编程最主要的工作就是在发送端把信息通过规定好的协议进行包装,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,从而达到通信的目的。
1 什么是套接字?
套接字(Socket)是TCP/IP的网络通信的基本单元,简单来说就是通信双方的一种约定。TCP用主机的IP地址加上主机上的端口作为TCP连接的端点,即表示为“IP地址:端口号”,例如Tomcat服务器常见的localhost:8080。
套接字是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的是些、本地主机IP地址、本地进程的协议端口、目的主机的IP地址、目的主机进程的协议端口。某个程序将一段信息写入套接字中,该套接字就会将这段信息发送给另外一个套接字接收这段信息。所以,套接字编程也被称为Socket编程。
2 Java.net包
在Java的API中,java.net包是用来提供网络服务的。包中包含有各种专门用于开发网络应用程序的类,java.net包大致分为以下两个部分。
低级API:用于处理网络地址(网络标识符,如IP地址)、套接字(基本双向数据通信机制)和接口(用于描述网络接口)
高级API:用于处理URI(统一资源标识符)、URL(统一资源定位符)、URLConnection连接(表示到URL所指向资源的连接)等
一、InetAddress类
Internet上的主机都有IP地址和当地DNS能够解析的域名。在java.net包中提供了IP地址的封装类InetAddress。
InetAddress类用于秒速和包装一个InternetIP地址,并提供了相关的常用方法,例如:解析IP地址的主机名称、获取本地IP地址的封闭、测试指定IP地址是否可达等。InetAddress还有两个子类:Inet4Address、Inet6Address它们分别代表IPv4和IPv6地址。
1.1 创建InetAddress对象
InetAddress实现了java.io.Serializable接口,不允许继承,所以也就没有构造器,而是提供了如下5个方法返回InetAddress实例。
方法 | 功能描述 |
getLocalhost() | 返回封装本地地址的实例 |
getByName(String host) | 返回一个封装Host地址的实例。其中,Host可以是域名或IP地址。 |
getAllByName(String host) | 返回一个封装Host地址的InetAddress实例数组。 |
getByAddress(byte[] addr) | 返回一个给定原始IP地址的字节数组所创建的InetAddress对象。 |
getByAddress(String host,byte[] addr) | 返回根据指定主机名和IP地址所创建的InetAddress对象。 |
在这些静态方法中,最为常用的应该是getByName(String host)方法,当传入目标主机的名字或IP地址之后,InetAddress会尝试连接DNS服务器,并且获取IP地址的操作。程序范例如下所示:
//[方式一]通过“方法”获取本地主机的InetAddress对象
InetAddress addr1 = InetAddress.getLocalHost();
//[方式二]根据“域名”创建InetAddress对象
InetAddress addr2 = InetAddress.getByName("www.baidu.com");
//[方式三]根据“IP”创建InetAddress对象
InetAddress addr3 = InetAddress.getByName("14.215.177.38");
//[方式四]根据“主机名”创建InetAddress对象
InetAddress addr4 = InetAddress.getByName("DESKTOP-U7IA5UV");
//[方式5]通过“主机名”获取InetAddress数组对象
InetAddress[] addr5 = InetAddress.getAllByName("www.baidu.com");
for(InetAddress addr:addr5){
System.out.println(addr);
}
//[方式六]通过“指定主机名和ip字节数组”创建InetAddress对象
InetAddress addr6 = InetAddress.getByAddress("Laoye",new byte[]{-64,-88,0,1});
每个对象的输出结果如下图所示:
需要注意的是,这些方法可能会抛出异常。如果安全管理器不允许访问DNS服务器或者禁止网络连接,则会抛出SecurityException异常;如果找不到对应主机IP地址或发生其他网络I/O错误,则会抛出UnknowHostException异常。所以需要在方法中使用throws抛出异常或者使用try-catch来捕捉异常。
//第一种方法
public void method() throws UnknownHostException{
}
//第二种方法
try{
//代码执行体......
}catch(UnknownHostException e){
e.printStackTrace();
}
1.2 判断IP类型
初次之外,InetAddress还提供了如下三个方法来获取InetAddress实例对应的IP地址和主机名。
方法 | 功能描述 |
getCanonicalHostName() | 获取此IP地址的全限定域名 |
getHostName() | 获取此IP地址的主机名 |
getHostAddress() | 获取该InetAddress实例对应的IP地址字符串 |
getAddress() | 获取该InetAddress实例的原始IP地址 |
范例程序如下所示。
InetAddress addr = InetAddress.getLocalHost();
System.out.println("主机名:"+addr.getHostName());
System.out.println("IP地址:"+addr.getHostAddress());
输出结果为。
主机名:DESKTOP-U7IA5UV
IP地址:192.168.243.1
需要注意的是,InetAddress类没有对应的setHostName()和setAddress()方法,这说明java.net之外的包无法在后台修改InetAddress对象的字段。这使得InetAddress不可变,因此线程是安全的。
我们可以通过getAddress()获取该IP的字节数组,然后再判断长度来判断是IPv4还是IPv6,例如下面程序所示。
InetAddress local = InetAddress.getLocalHost();
byte[] address = local.getAddress();
if(address.length == 4){ //或者local.getAddress().length
System.out.println("IPv4");
}else if(address.length == 16){
System.out.println("IPv6");
}else{
System.out.println("啥都不是");
}
Inet4Address和Inet6Address中的哪一个实例。
if(local instanceof Inet4Address){
System.out.println("local对象是Inet4Address类的实例");
}else if(local instanceof Inet6Address){
System.out.println("local对象是Inet6Address类的实例");
}else{
System.out.println("啥都不是");
}
1.3 地址类型
有些IP地址和地址模式有特殊的含义。例如:127.0.0.1是本地回送地址、224.0.0.0到239.255.255.255范围内的IPv4地址是组播地址。
返回类型 | 方法与描述 |
boolean | isAnyLocalAddress() 用于检查通配符地址中的InetAddress的实用程序例程。 |
boolean | isLinkLocalAddress() 用于检查InetAddress是否为链接本地地址的实用例程。 |
boolean | isLoopbackAddress() 用于检查InetAddress是否为环回地址的实用程序例程。 |
boolean | isMCGlobal() 用于检查多播地址是否具有全局范围的实用例程。 |
boolean | isMCLinkLocal() 用于检查多播地址是否具有链接范围的实用例程。 |
boolean | isMCNodeLocal() 用于检查多播地址是否具有节点范围的实用例程。 |
boolean | isMCOrgLocal() 用于检查多播地址是否具有组织范围的实用例程。 |
boolean | isMCSiteLocal() 用于检查多播地址是否具有站点范围的实用程序例程。 |
boolean | isMulticastAddress() 用于检查InetAddress是否为IP多播地址的实用程序例程。 |
boolean | isReachable(int timeout) 测试该地址是否可访问。 |
boolean | isSiteLocalAddress() 用于检查InetAddress是否为站点本地地址的实用程序例程。 |
1.4 缓存
DNS查找的开销相当大(如果请求需要经过多个中间服务器,或者尝试解析一个不可达的主机,大约需要几秒的事件),所以InetAddress类会缓存查找的结果,只要程序允许期间所存储主机的IP地址没有发生改变,每次创建一个新的InetAddress对象就不会去访问DNS服务器,而是直接通过缓存创建对象。
networkaddress.cache.ttl和networkaddress.cache.negative.ttl系统属性可以控制缓存时间。其中,前者指定了成功的DNS查询结果在Java缓存中保留的时间(秒),而后者指定了不成功的查找结果缓存时间(秒)。在指定时限内,再次查找相同的主机会返回相同的值,-1解释为永不过期。
1.5 测试可达性
InetAddress类有两个isReachable()方法,可以测试当前主机是否可以到达指定目的节点,建立网络连接。连接可能由于很多原因而阻塞,例如防火墙、代理服务器、行为失常的路由器、线缆断开、远程主机没有开机等原因。
boolean | isReachable(int timeout) |
boolean | isReachable(NetworkInterface interface,int ttl, int timeout) |
这两个方法尝试使用traceroute(准确来讲,就是ICMP echo请求)查看指定地址是否可达。如果主机再timeout毫秒内响应,则方法返回true;否则返回false,如果网络出现错误则抛出IOException异常。
第二个方法还允许指定从那个本地网络接口建立连接,以及“生存时间”(连接被丢弃前尝试的最大网络跳数)。
InetAddress addr = InetAddress.getByName("www.baidu.com");
if(addr.isReachable(10)){
System.out.println("在10毫秒内响应");
}else{
System.out.println("在10毫秒内没有响应");
}
二、NetworkInterface类
NetworkInterface类表示一个本地IP地址。这个类可以得到本机所有物理接口(以太网卡等)和虚拟接口的信息。
NetworkInerface对象表示物理硬件和虚拟地址,所以不能任意构造。与InetAddress类一样,有一些静态方法通过名字、IP地址或枚举,返回与某个网络接口关联的NetworkInerface对象。
方法 | 功能描述 |
getNetworkInterface() | 获取当前主机中的所有接口 |
getByName(String name) | 返回指定名字的网络接口 |
getByInetAddress(InetAddress addr) | 返回与指定IP地址绑定的网络接口 |
2.1 获取NetworkInterface对象
方式一:getNetworkInterface()
该方法返回一个Enumeration,里面存储着本地主机上的所有网络接口,例如下面的程序范例。
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while(interfaces.hasMoreElements()){
NetworkInterface ni = interfaces.nextElement();
System.out.println(ni);
}
输出结果
方式二:getByName(String name)
NetworkInerface对象,表示有指定名字的网络接口。如果没有这个接口,就返回null。如果在查找相关网络接口时底层网络栈遇到问题,会抛出SocketException异常,不过这种情况不太可能发生。
NetworkInterface ni = NetworkInterface.getByName("lo");
方式三:getByInetAddress(InetAddress address)
getByInetAddress()方法返回一个NetworkInterface对象,表示与指定IP地址绑定的网络接口。如果本地主机上没有网络接口与这个IP地址绑定,就返回null。如果发生错误,就抛出SocketException异常。
InetAddress local = InetAddress.getLocalHost();
NetworkInterface ni = NetworkInterface.getByInetAddress(local);
输出结果
name:eth5 (VMware Virtual Ethernet Adapter for VMnet8)
2.2 获取方法
NetworkInerface对象之后,就可以查询该对象的IP地址和名字,这几乎是这些对象所能完成的唯一操作。
方法一:getInetAddresses()
一个网络接口可以绑定多个IP地址,通过该方法可以返回一个Enumeration对象,里面包含了与这个接口绑定的每一个IP地址都包含一个InetAddress。范例代码如下所示。
//指定接口eho0创建NetworkInterface对象
NetworkInterface eth0 = NetworkInterface.getByName("eth0");
//调用getInetAddresses()方法获取该接口的所有对象
Enumeration addresses = eth0.getInetAddresses();
//遍历Enumeration对象
while(addresses.hasMoreElements()){
System.out.println(addresses.nextElement());
}
输出结果如下所示:
/192.168.141.1
/fe80:0:0:0:9569:dc97:e0ba:4adf%eth0
方法二:getName()
NetworkInerface对象的接口名,范例代码如下所示。
//指定接口eho0创建NetworkInterface对象
NetworkInterface eth0 = NetworkInterface.getByName("eth0");
//调用该对象的getName()方法获取该对象的接口名
System.out.println(eth0.getName());
方法三:getDisplayName()
NetworkInerface对象的一个更友好的名字,类似于“Ethernet Card 0”。不过,在UNIX上,它总是返回于getName()同样的结果,在Windows上,就可以看到稍微友好一点的名字。范例代码如下所示:
//指定接口eho0创建NetworkInterface对象
NetworkInterface eth0 = NetworkInterface.getByName("eth0");
//调用该对象的getName()方法获取该对象的接口名
System.out.println(eth0);
System.out.println(eth0.getName());
System.out.println(eth0.getDisplayName());
输出结果如下所示:
name:eth0 (VMware Virtual Ethernet Adapter for VMnet1)
eth0
VMware Virtual Ethernet Adapter for VMnet1