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; ...
随机推荐
- Maven相关:mvn的配置和简单命令
1.配置mvn: 点击链接 http://maven.apache.org/download.cgi 下载 解压到文件夹: 配置系统环境变量: 测试:mvn -v 2.mvn命令: 1. 显示版本信息 ...
- docker---安装docker
今天开始要初步的学习 docker 了,这也是进入虚拟化方向的一个步骤,学习一个东西首先要从搭建环境开始,哈哈 安装环境及版本: 系统:Ubuntu18.04 STL Docker 版本:19.03. ...
- Java clone方法的使用
浅克隆 Person p2 = (Person) p1.clone(); clone()方法使用后得到p2,p2和p1指向不同的地址.但是如果p1中的属性是引用类型,那么不再对这个引用类型进行复制,而 ...
- KMP算法复杂度证明
引言 KMP算法应该是看了一次又一次,比赛的时候字符串不是我负责,所以学到的东西又还给网上的博客了-- 退役后再翻开看,看到模板,心想这不是\(O(n^2)\)的复杂度吗? 有两个循环也不能看做是\( ...
- CF 1096D Easy Problem [动态规划]
题目链接:http://codeforces.com/problemset/problem/1096/D 题意: 有一长度为n的字符串,每一字符都有一个权值,要求现在从中取出若干个字符,使得字符串中没 ...
- 架构之道(3) - 令後端的吐血和喊FUCK的次数锐减
「那个产品经理不会技术,整天在需求,真操他妈的.」 这是很多产品经理遇到的一句话,如果你把顾客阶段完成了,回到自己的团队,遇到个技术大牛这麽说,那就表示,自己作为产品经理的功力还不够. 等我慢现解释, ...
- docker可视化工具
介绍两款Docker可视化工具 DockerUI(特点:轻量) 下载镜像 docker pull abh1nav/dockerui 启动容器 docker run -d --privileged -- ...
- InsertionSort(插入排序)原理及C++代码实现
插入排序是最常用的排序之一. 在输入规模较小的时候,插入排序的性能较好. 最好情况下插入排序的时间复杂度是O(n),平均情况则为O(n2). 插入排序是稳定的排序算法之一. 基本思路为从第二个元素开始 ...
- LeetCode No.154,155,156
No.154 FindMin 寻找旋转排序数组中的最小值 II 题目 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7 ...
- mplayer 的安装步骤
编译mplayer: make distclean ./configure --disable-png --disable-gif //加后面的是因为编译时出错了,也可以直接 ./con ...