标签:Java addr ai IP 域名解析 host 源码 hosts 地址
前言
在互联网中通信需要借助 IP 地址来定位到主机,而 IP 地址由很多数字组成,对于人类来说记住某些组合数字很困难,于是,为了方便大家记住某地址而引入主机名和域名。 早期的网络中的机器数量很少,能很方便地通过 hosts 文件来完成主机名称和 IP 地址的映射,这种方式需要用户自己维护网络上所有主机的映射关系。后来互联网迅猛发展起来,hosts 文件方式已经无法胜任,于是引入域名系统(DNS)来解决主机名称和 IP 地址的映射。 局域网中常用来表示 IP 地址的名称更多称为主机名,而互联网上用来表示 IP 地址的名称更多称为域名。核心内容都相同,都是解决名称和 IP 地址间的映射。 Java 中提供了很多互联网主机名称和地址操作相关的接口,现在来看看 JDK 内部对域名解析相关功能的实现。其实,InetAddress 类内部存在一个 NameService 内部接口用于实现域名及IP的映射。 对于 JDK 主要使用了两种映射解析方案,一种是 hosts 文件机制,另外一种是操作系统自带的解析方案。相关类
[Java] 纯文本查看 复制代码 ?1 2 3 |
--java.lang.Object
--java.net.InetAddress$HostsFileNameService
--java.net.InetAddress$PlatformNameService
|
JDK选择的方案
01 02 03 04 05 06 07 08 09 10 11 |
private static NameService createNameService() {
String hostsFileName =
GetPropertyAction.privilegedGetProperty( "jdk.net.hosts.file" );
NameService theNameService;
if (hostsFileName != null ) {
theNameService = new HostsFileNameService(hostsFileName);
} else {
theNameService = new PlatformNameService();
}
return theNameService;
}
|
接口定义
[Java] 纯文本查看 复制代码 ?1 2 3 4 5 6 7 |
private interface NameService {
InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException;
String getHostByAddr( byte [] addr) throws UnknownHostException;
}
|
NameService 接口主要定义了两个方法,用于获取主机名称对应的 IP 地址和 IP 地址对应的主机名称。
HostsFileNameService 类
类定义如下: [Java] 纯文本查看 复制代码 ?1 |
private static final class HostsFileNameService implements NameService
|
lookupAllHostAddr方法
该方法根据主机名称实现基于 hosts 文件的 IP 地址查找方案。它要完成的逻辑如下:* 根据指定的 hosts 文件路径扫描每一行,如果不存在文件则抛出 FileNotFoundException 异常。
* 遍历每行内容,如果以 # 号开头则表示该行为注释内容,直接忽略,否则继续。
* 标准情况下内容可以为 127.0.0.1 localhost #local,# 号后面为注释内容,所以调用 removeComments 方法去掉 #local,该方法不再贴出。
* 处理后的内容为127.0.0.1 localhost,接着看是否包含了传进来的主机名称有的话则说明是该主机名称映射的 IP 地址,通过 extractHostAddr 方法提取IP地址,值为 127.0.0.1,该方法不再贴出。
* 处理后的内容为127.0.0.1字符串,需要调用 createAddressByteArray 将其转换为 byte 数组以方便得到 InetAddress 对象,该方法不再贴出。
* 将得到的 添加到 ArrayList 对象中,最终转换为 InetAddress 数组并返回。 [Java] 纯文本查看 复制代码 ?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException {
String hostEntry;
String addrStr = null ;
InetAddress[] res = null ;
byte addr[] = new byte [ 4 ];
ArrayList<InetAddress> inetAddresses = null ;
try (Scanner hostsFileScanner = new Scanner( new File(hostsFile), "UTF-8" )) {
while (hostsFileScanner.hasNextLine()) {
hostEntry = hostsFileScanner.nextLine();
if (!hostEntry.startsWith( "#" )) {
hostEntry = removeComments(hostEntry);
if (hostEntry.contains(host)) {
addrStr = extractHostAddr(hostEntry, host);
if ((addrStr != null ) && (!addrStr.equals( "" ))) {
addr = createAddressByteArray(addrStr);
if (inetAddresses == null ) {
inetAddresses = new ArrayList<>( 1 );
}
if (addr != null ) {
inetAddresses.add(InetAddress.getByAddress(host, addr));
}
}
}
}
}
} catch (FileNotFoundException e) {
throw new UnknownHostException( "Unable to resolve host " + host
+ " as hosts file " + hostsFile + " not found " );
}
if (inetAddresses != null ) {
res = inetAddresses.toArray( new InetAddress[inetAddresses.size()]);
} else {
throw new UnknownHostException( "Unable to resolve host " + host
+ " in hosts file " + hostsFile);
}
return res;
}
|
getHostByAddr方法
该方法根据 IP 地址实现基于 hosts 文件的主机名称查找方案。它要完成的逻辑如下:* 传入的参数为 IP 地址的字节数组,比如new byte[] {127, 0, 0, 1},先调用 addrToString 方法将其转换为”127.0.0.1”字符串,该方法不再贴出。
* 根据指定的 hosts 文件路径扫描每一行,如果不存在文件则抛出 FileNotFoundException 异常。
* 遍历每行内容,如果以 # 号开头则表示该行为注释内容,直接忽略,否则继续。
* 标准情况下内容可以为 127.0.0.1 localhost #local,# 号后面为注释内容,所以调用 removeComments 方法去掉 #local,该方法不再贴出。
* 处理后的内容为127.0.0.1 localhost,接着看是否包含了传进来的 IP 地址,有的话则说明是该 IP 地址对应的主机名称,通过 extractHost 方法提取主机名称localhost,该方法不再贴出。
* 一旦找到主机名称后则不再往下遍历,跳出循环并返回主机名称。 [Java] 纯文本查看 复制代码 ?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public String getHostByAddr( byte [] addr) throws UnknownHostException {
String hostEntry;
String host = null ;
String addrString = addrToString(addr);
try (Scanner hostsFileScanner = new Scanner( new File(hostsFile), "UTF-8" )) {
while (hostsFileScanner.hasNextLine()) {
hostEntry = hostsFileScanner.nextLine();
if (!hostEntry.startsWith( "#" )) {
hostEntry = removeComments(hostEntry);
if (hostEntry.contains(addrString)) {
host = extractHost(hostEntry, addrString);
if (host != null ) {
break ;
}
}
}
}
} catch (FileNotFoundException e) {
throw new UnknownHostException( "Unable to resolve address "
+ addrString + " as hosts file " + hostsFile
+ " not found " );
}
if ((host == null ) || (host.equals( "" )) || (host.equals( " " ))) {
throw new UnknownHostException( "Requested address "
+ addrString
+ " resolves to an invalid entry in hosts file "
+ hostsFile);
}
return host;
}
|
PlatformNameService类
类定义如下:
1 |
private static final class PlatformNameService implements NameService
|
该类即是对操作系统自带的解析方案的封装,核心的两个方法如下,因为这两个方法与操作系统相关,所以通过它们通过 InetAddressImpl 接口调用了对应的本地方法,本地方法分别为 lookupAllHostAddr 和 getHostByAddr。
1 2 3 4 5 6 7 |
public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException{
return impl.lookupAllHostAddr(host);
}
public String getHostByAddr( byte [] addr) throws UnknownHostException{
return impl.getHostByAddr(addr);
}
|
lookupAllHostAddr方法
结构 | 参数 |
typedef struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char* ai_canonname; struct sockaddr* ai_addr; struct addrinfo* ai_next; } | ai_addrlen must be zero or a null pointer ai_canonname must be zero or a null pointer ai_addr must be zero or a null pointer ai_next must be zero or a null pointer ai_flags:AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST ai_family: AF_INET,AF_INET6 ai_socktype:SOCK_STREAM,SOCK_DGRAM ai_protocol:IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc. |
getHostByAddr方法
该本地方法用于根据 IP 地址获取主机名,传入的参数为 byte[],返回为字符串。它要完成的工作就是通过操作系统提供的主机名称服务接口获取主机名,然后返回字符串。 Windows 和 unix-like 操作系统实现的代码都差不多,这里只贴出 Windows的,基本的逻辑为:先通过 JNI 的 GetByteArrayRegion 函数获取传入的4个字节,这里因为字节可能是负数,所以需要进行移位操作;然后通过 getnameinfo 函数获取主机名;最后通过 JNI 的 NewStringUTF 函数将主机名放到新建的字符串对象中。 [Java] 纯文本查看 复制代码 ?01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this ,
jbyteArray addrArray) {
jstring ret = NULL;
char host[NI_MAXHOST + 1 ];
jbyte caddr[ 4 ];
jint addr;
struct sockaddr_in sa;
memset(( char *)&sa, 0 , sizeof(struct sockaddr_in));
(*env)->GetByteArrayRegion(env, addrArray, 0 , 4 , caddr);
addr = ((caddr[ 0 ] << 24 ) & 0xff000000 );
addr |= ((caddr[ 1 ] << 16 ) & 0xff0000 );
addr |= ((caddr[ 2 ] << 8 ) & 0xff00 );
addr |= (caddr[ 3 ] & 0xff );
sa.sin_addr.s_addr = htonl(addr);
sa.sin_family = AF_INET;
if (getnameinfo((struct sockaddr *)&sa, sizeof(struct sockaddr_in),
host, NI_MAXHOST, NULL, 0 , NI_NAMEREQD)) {
JNU_ThrowByName(env, "java/net/UnknownHostException" , NULL);
} else {
ret = (*env)->NewStringUTF(env, host);
if (ret == NULL) {
JNU_ThrowByName(env, "java/net/UnknownHostException" , NULL);
}
}
|
标签:Java,addr,ai,IP,域名解析,host,源码,hosts,地址 来源: https://www.cnblogs.com/zhuxiaopijingjing/p/12271918.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。