JDK源码看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; } |
接口定义
|
1
2
3
4
5
6
7
|
private interface NameService { InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException; String getHostByAddr(byte[] addr) throws UnknownHostException; } |
HostsFileNameService 类
|
1
|
private static final class HostsFileNameService implements NameService |
lookupAllHostAddr方法
* 根据指定的 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 数组并返回。
|
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 地址的字节数组,比如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,该方法不再贴出。
* 一旦找到主机名称后则不再往下遍历,跳出循环并返回主机名称。
|
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方法
|
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); } } |
JDK源码看Java域名解析的更多相关文章
- 结合JDK源码看设计模式——桥接模式
前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...
- 结合JDK源码看设计模式——原型模式
定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...
- 结合JDK源码看设计模式——单例模式
定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...
- 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂
三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...
- JDK源码看ArrayList和Vector的一些区别
最近在看JDK源码,从源码的角度记录一下ArrayList和Vector的一些区别 1.new a.不指定长度 Vector默认创建10个元素的数组 public Vector() { this(10 ...
- 从源码看Java集合之ArrayList
Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...
- 结合JDK源码看设计模式——模板方法模式
前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...
- 结合JDK源码看设计模式——适配器模式
定义: 将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作适用场景: 已经存在的类,它的方法和需求不匹配的时候 在软件维护阶段考虑的设计模式 详解 首 ...
- 从源码看java中Integer的缓存问题
在开始详细的说明问题之前,我们先看一段代码 public static void compare1(){ Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; ...
随机推荐
- python format输出
http://www.cnblogs.com/nulige/p/6115793.html 2.Format 方式 [[fill]align][sign][#][0][width][,][.precis ...
- GUI(Graphical User Interface)
译:用户和图形界面 GUI与程序交互的不同方式,包含3基本要素:输入,处理和输出. 常用GUI框架包括以下几种: wxPython Kivy Flexx PyQt Tkinter Pywin32 Py ...
- 吴裕雄--天生自然ShellX学习笔记:Shell 传递参数
在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 实例 以下实例我们向脚本传递三个参数, ...
- 吴裕雄--天生自然Linux操作系统:Linux 文件与目录管理
Linux的目录结构为树状结构,最顶级的目录为根目录 /. 其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们. 绝对路径: 路径的写法,由根目录 / 写起,例如: /usr/share/ ...
- maven deploy到私服去掉时间戳
直接上代码在插件中的配置. <plugin> <artifactId>maven-war-plugin</artifactId> <version>3. ...
- Python学习中的随笔,好记性不如烂笔头
本文 为博主看了 vamei 的blog 写下的随笔 . 致敬Vamei 1.type() 可以显示参数的类型 如 : a=12 type(a) 为 int 2.python的基本类型 为 i ...
- Python的IDE之Pycharm的使用
Python的IDE之Pycharm的使用 一.为什么用IDE(Python集成开发环境-Pycharm) 到现在为止,我们也是写过代码的人啦,但你有没有发现,每次写代码要新建文件.写完保存时还要选择 ...
- VMware12 + Ubuntu16.04 虚拟磁盘扩容
转载自:https://blog.csdn.net/Timsley/article/details/50742755 今天用虚拟机的时候,发现虚拟机快满了,提示磁盘空间小,不得不扩充虚拟机空间.经过百 ...
- Java常用的公共方法
--获取规字符串中的指定名称的某个字段值 1.public String getValueByName(String params,String name) --用于通过表单选中的复选框获取它的值(j ...
- Java--平台版本、跨平台、JVM、JDK、JRE
Java2平台版本 Java2平台包括标准版(J2SE).企业版(J2EE)和微缩版(J2ME)三个版本 J2SE 包含那些构成Java语言核心的类. J2EE 包含J2SE 中的类,并且还包含用于开 ...