连接到Internet的设备称为节点(node),计算机节点称为主机(host),每个节点或主机都由至少一个唯一的数来标识,这称为Internet地址或IP地址。目前大多数IP地址是四字节长,这被称为IPv4地址,还有的IP地址是16字节长,这被称作IPv6地址。IP比较难记,Internet的设计者发明了域名系统(Domain Name System,DNS)跟IP关联在一起,每台连接到Internet的计算机都应当能访问域名服务器(domain name server),一个运行特殊DNS软件的UNIX主机,这种软件了解不同主机名和IP地址之间的映射。
1、InetAddress类
java.net.InetAddress类是Java对IP地址的高层表示,其他网络类如Socket、URL、DatagramSocket都要用到这个类,它包括一个主机名和一个IP地址。InetAddress类没有公共构造函数,最常用的是InetAddress.getByName(),可以如下查找money.innovatelife.net
InetAddress address = InetAddress.getByName("money.innovatelife.net");
它会建立与本地DNS服务器的一个连接,来查找名字和数字地址(如果之前查找过这个主机,信息可能会本地缓存,这样就不需要再建立网络连接了)。如果DNS服务器找不到这个地址,这个方法会抛出UnknownHostException(IOException的一个子类)。还可以按IP地址反向查找,如果你查找的地址没有相应的主机名,通过getHostName()方法就会返回你提供的IP地址。
如果你需要得到一个主机的所有地址,可以调用getAllByName(),它会返回一个数组。getLoaclHost()方法尝试连接DNS来得到一个真正的主机名和IP地址,如“zhaoyi-pc/10.10.7.154”;如果失败它就会返回回送地址,即“localhost/127.0.0.1”。
从主机名创建一个新的InetAddress对象被认为是一个潜在的不安全操作,因为这需要一个DNS查找。任意DNS查找会泄露信息,所以要禁止任意的DNS查找。
有些IP地址和地址模式有特殊的含义,如127.0.0.1是本地回送地址,224.0.0.0到239.255.255.255范围内的IPv4是组播地址,可以同时发送到多个订购的主机。Java提供了10个方法来测试InetAddress对象是否符合其中某个标准:
- 如果地址是通配地址(wildcard address),insAnyLocalAddress()方法返回true。通配地址可以匹配本地系统中的任何地址。IPv4中通配地址是0.0.0.0,IPv6中通配地址是0:0:0:0:0:0:0:0(又写作::)。
- 如果地址是回送地址(loopback address),isLoopbackAddress()方法返回true。回送地址之间在IP层连接同一台计算机,而不使用任何物理硬件。IPv4中通配地址是127.0.0.1,IPv6中通配地址是0:0:0:0:0:0:0:1(又写作::1)。
- 如果地址是一个IPv6本地链接地址,isLinkLocalAddress()方法返回true。IPv6本地链接地址可以用于帮助IPv6网络实现自配置,与IPv4网络上的DHCP非常相似,但没有必要使用服务器。路由器不会把发送给本地链接地址的包转发到本地子网之外。
- 如果地址是一个IPv6本地网站地址,isSiteLocalAddress()方法返回true。与本地链路地址相似,不过本地网站地址可以由路由器在网站或校园网内转发,但不应转发到网站之外。
- 如果地址是一个组播地址,isMulticastAddress()方法返回true。组播会将内容广播给所有预订的计算机,而不是某一台计算机。第13章将讨论组播。
2、Inet4Address和Inet6Address
Java使用了Inet4Address和Inet6Address来区分IPv4地址和IPv6地址,这两个类继承了InetAddress。在Java程序所在的应用层,完全不需要考虑一个地址是IPv4地址还是IPv6地址
3、NetworkInterface类
NetworkInterface类表示一个本地IP地址。这可以是一个物理接口,如额外的以太网卡(常见于防火墙和路由器),也可以是一个虚拟接口,与机器的其他IP地址绑定到同一个物理硬件。
在典型的UNIX系统上,以太网接口名的形式为eth0、eth1等,本地回送地址的名字可能类似于“lo”。在Windows上,名字是类似“CE31”和“ELX100”的字符串,取自这个特定网络接口的厂商名和硬件模型名。下面的代码段尝试找到UNIX系统上的主以太网接口:
try {
NetworkInterface networkInterface = NetworkInterface.getByName("eth0");
if(networkInterface==null){
System.err.println("没有这样的接口");
}
} catch ( SocketException e) {
System.err.println(e.getMessage());
}
getByInetAddress()方法返回一个NetworkInterface对象,表示与指定IP地址绑定的网络接口。 getNetworkInterfaces()方法会列出本地主机上的所有网络接口。
有了NetworkInterface对象,就可以查询其IP地址和名字。getInetAddresses()返回一个网络接口绑定的IP地址,getName()返回某个特定NetworkInterface对象的名,如eth0或lo。
4、一些有用的程序
使用java.net.InetAddress类中的工具就能编写一些相当有用的程序,这里可以看到一个例子:检查地址是否是一个已知的垃圾邮件发送者。
SpamCheck:很多服务会监视垃圾邮件发送者(spammer),并通知客户端试图与之连接的主机是否是一个已知的垃圾邮件发送者。如果请求实时黑洞列表(real-time blackhole lists,RBL)需要很快做出响应,而且要处理相当大的工作负载。看起来可以使用Web服务器、SOAP、UDP、定制协议或者其他某种机制来实现,不过实际上只使用DNS就能巧妙地实现这个服务。如你想向sbl.spamhaus.org询问207.87.34.17是否是一个垃圾邮件发送者,就要查询主机名17.34.87.207.sbl.spamhaus.org(逆置地址的字节)。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class SpamCheck {
public static final String BLACKHOLE = "sbl.spamhaus.org";
private static boolean isSpammer(String host) {
try {
InetAddress address = InetAddress.getByName(host);
byte[] quad = address.getAddress();
String query = BLACKHOLE;
for (byte octet : quad) {
int unsignedByte = octet < 0 ? octet + 256 : octet;
query = unsignedByte + "." + query;
}
InetAddress.getByName(query);
return true;
} catch (UnknownHostException e) {
return false;
}
}
public static void main(String[] args) {
isSpammer("17.34.87.207");
}
}