1. 定义
连接到Internet的设备称为节点(node)。计算机节点称为主机(host)。每个节点或主机都由至少一个唯一的数来标识,这称为Internet地址或IP地址。
由于IP地址太长,Internet的设计者发明了域名系统DNS。将人们可以记忆的主机名与计算机可以记忆的IP地址关联在一起。
2. InetAddress类
java.net.InetAddres s类是Java对IP地址(包括IPv4和IPv6)的高层表示。大多数其他网络类都要用到这个类,包括Socket、ServerSocket、URL、DatagramSocket、 DatagramPacket等。一般地讲,它包括一个主机名和一个IP地址。
2.1 创建新的InetAddress对象
InetAddress类没有公共构造函数。实际上,InetAddress有一些静态工厂方法,可以连接到DNS服务器来解析主机名。最常用的是InetAddress.getByName()。
显示www.baidu.com地址的程序。
package com.zyz.internet;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 显示www.baidu.com地址的程序
*/
public class BaiduByName {
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getByName("www.baidu.com");
System.out.println(address);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
可以用InetAddress.getLocalHost()方法查找本机的信息。
查找本地机器的地址。
package com.zyz.internet;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 查找本地机器的地址
*/
public class MyAddress {
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getLocalHost();
System.out.println(address);
} catch (UnknownHostException e) {
System.out.println("Could not find this computer's address");
}
}
}
输出如下:
zhangyunzhengdeMacBook-Pro.local/127.0.0.1
2.2 缓存
由于DNS查找的开销可能相当大(如果请求需要经过多个中间服务器,或者尝试解析一个不可达的主机,这大约需要几秒的时间),所以InetAddress类会缓存查找的结果。一旦得到一个给定主机的地址,就不会再次查找,即使你为同一个主机创建一个新的 InetAddress对象,也不会再次查找地址。只要在程序运行期间IP地址没有改变,这就没有问题。
可以通过该方法进行设置。
java.security.Security.setProperty("networkaddress.cache.ttl", "0");
2.3 按IP地址查找
主机名要比IP地址稳定得多。有些服务多年以来一直使用同一个主机名,但IP地址更换了很多次。如果要在使用主机名(如www.oreilly.com)或使用IP地址(如208.201.239.37)之间做出选择,一定要选择主机名。只有当主机名不可用时才使用IP地址。
2.4 获取方法
InetAddress包含4个获取方法,可以将主机名作为字符串返回,将IP地址返回为字符串和字节数组:
public String getHostName();
public String getCanonicalHostName();
public byte[] getAddress()
public String getHostAddress()
没有对应的setHostName()和setAddress()方法,这说明java.net之外的包无法在后台改变InetAddress对象的字段。这使得InetAddress不可变,因此是线程安全的。
给定地址,找出主机名。
package com.zyz.internet;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class ReverseTest {
public static void main(String[] args) throws UnknownHostException {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress.getCanonicalHostName());
}
}
输出:
localhost
getHostAddress()方法返回一个字符串,其中包含点分四段格式的IP地址。
package com.zyz.internet;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 找到本机对应的IP地址
*/
public class MyIPAddress {
public static void main(String[] args) {
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
System.out.println("My address is " + inetAddress.getHostAddress());
}
}
输出:
My address is 127.0.0.1
与C不同,Java没有无符号字节这种基本数据类型。值大于127的字节会当作负数。因此,如果要对getAddress()返回的字节做任何处理,需要将字节提升为int,并做适当的调整。下面给出一种做法∶
int unsignedByte = signedByte < 0 ? signedByte + 256 : signedByte;
这里,signedByte可能为正也可能为负。条件操作符?测试signedByte是否为负。如果为负,则为signedByte加上256使其成为正数。否则保持不变。signedByte会在完成加法操作之前自动提升为int,所以不存在这种回绕(wraparound)问题。
确定IP地址是IPv4还是IPv6
package com.zyz.internet;
import java.net.InetAddress;
public class AddressTests {
public static int getVersion(InetAddress inetAddress){
byte[] address = inetAddress.getAddress();
if (address.length == 4) return 4;
else if(address.length == 16) return 6;
else return -1;
}
}
2.5 测试可达性
InetAddress类有两个isReachable()方法,可以测试一个特定节点对当前主机是否可达(也就是说,能否建立一个网络连接)。连接可能由于很多原因而阻塞,包括防火墙、代理服务器、行为失常的路由器和断开的线缆等,或者只是因为试图连接时远程主机没有开机。
public boolean isReachable(int timeout) throws IOException
public boolean isReachable(NetworkInterface netif, int ttl,
int timeout) throws IOException
这些方法尝试使用traceroute(更确切地讲,就是ICMPecho请求)查看指定地址是否可达。如果主机在timeout毫秒内响应,则方法返回true;否则返回false。如果出现网络错误则抛出IOException异常。第二个方法还允许指定从哪个本地网络接口建立连接,以及"生存时间"(连接被丢弃前尝试的最大网络跳数)。
2.6 eauals方法
InetAddress重写了Object类的equals方法,如果两个InetAddress对象有相同的IP地址,只有此时才会与该InetAddress对象相等。
public boolean equals(Object obj) {
return (obj != null) && (obj instanceof Inet4Address) &&
(((InetAddress)obj).holder().getAddress() == holder().getAddress());
}
2.7 Inet4Address和Inet6Address
Java使用了两个类Inet4Address和Inet6Address,来区分IPv4地址和IPv6地址:
public final class Inet4Address extends InetAddress
public final class Inet6Address extends InetAddress
2.8 NetworkInterface类
NetworkInterface类表示一个本地IP地址。这可以是一个物理接口,如额外的以太网卡(常见于防火墙和路由器),也可以是一个虚拟接口,与机器的其他IP地址绑定到同一个物理硬件。NetworkInterface类提供了一些方法可以枚举所有本地地址(而不考虑接口),并由它们创建InetAddress对象,然后这些InetAddress对象可用于创建socket、服务器socket等。
2.8.1 工厂方法
由于NetworkInterface对象表示物理硬件和虚拟地址,所以人不能任意构造.与Inet Address类一样,有一些静态工厂方法可以返回与某个网络接口关联的 NetworkInterface对象。可以通过IP地址、名字或枚举来请求一个NetworkInterface。
getByName()方法返回一个NetworkInterface对象,表示有指定名字的网络接口。如果没有这样一个接口,就返回null。如果在查找相关网络接口时底层网络栈遇到问题,会抛出一个SocketException异常,不过这种情况不太可能发生。
public static NetworkInterface getByName(String name) throws SocketException
getByInetAddress()方法返回一个NetworkInterface对象,表示与指定IP地址绑定的网络接口。如果本地主机上没有网络接口与这个IP地址绑定,就返回null。如果发生错误,就抛出一个SocketException异常。
public static NetworkInterface getByInetAddress(InetAddress addr) throws SocketException
列出所有网络接口的程序
package com.zyz.internet;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* 列出所有网络接口的程序
*/
public class InterfaceLister {
public static void main(String[] args) throws SocketException {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()){
NetworkInterface networkInterface = networkInterfaces.nextElement();
System.out.println(networkInterface);
}
}
}
输出:
name:utun3 (utun3)
name:utun2 (utun2)
name:utun1 (utun1)
name:utun0 (utun0)
name:llw0 (llw0)
name:awdl0 (awdl0)
name:en5 (en5)
name:en7 (en7)
name:en0 (en0)
name:lo0 (lo0)
2.8.2 获取方法
有了NetworkInterface对象,就可以查询其IP地址和名字。
返回该网络接口的所有IP地址
public Enumeration<InetAddress> getInetAddresses()
package com.zyz.internet;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
public class GetNetworkIP {
public static void main(String[] args) throws SocketException {
NetworkInterface en0 = NetworkInterface.getByName("en0");
Enumeration<InetAddress> inetAddresses = en0.getInetAddresses();
while (inetAddresses.hasMoreElements()){
System.out.println(inetAddresses.nextElement());
}
}
}
返回某个特定NetworkInterface对象的名,如eth0。
public String getName()
返回特定NetworkInterface的一个更友好的名字。
public String getDisplayName()