1. getByName

public static InetAddress getByName(String host) throws UnknownHostException {
return InetAddress.getAllByName(host)[0];
} public static InetAddress[] getAllByName(String host) throws UnknownHostException {
return getAllByName(host, null);
} private static InetAddress[] getAllByName(String host, InetAddress reqAddr) throws UnknownHostException {
// 1. host == null || host.length() == 0
// loopbackAddress(与系统属性java.net.preferIPv6Addresses相关):Inet4Address(localhost/127.0.0.1) || Inet6Address(localhost/::1)
// 2. host表示一个IP地址
// Inet4Address(null/IPAddressUtil.textToNumericFormatV4(host)) || Inet6Address(null/IPAddressUtil.textToNumericFormatV6(host)) ... ... return getAllByName0(host, reqAddr, true);
} private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check) throws UnknownHostException {
... ... InetAddress[] addresses = getCachedAddresses(host); // 在缓存中查找记录
if (addresses == null) {
addresses = getAddressesFromNameService(host, reqAddr); // 请求域名解析
}
if (addresses == unknown_array) // 缓存中存在失败记录 || 域名解析失败
throw new UnknownHostException(host);
return addresses.clone();
}

1)InetAddress.Cache

static final class CacheEntry {
CacheEntry(InetAddress[] addresses, long expiration) {
this.addresses = addresses;
this.expiration = expiration;
}
InetAddress[] addresses; // 地址
long expiration; // 过期时间
} static final class Cache {
private LinkedHashMap<String, CacheEntry> cache;
private Type type; enum Type {Positive, Negative}; public Cache(Type type) {
this.type = type;
cache = new LinkedHashMap<String, CacheEntry>();
} private int getPolicy() { // 缓存过期时间
if (type == Type.Positive) {
return InetAddressCachePolicy.get(); // -Dsun.net.inetaddr.ttl=-1(启动参数) || networkaddress.cache.ttl=-1(配置文件)
} else {
return InetAddressCachePolicy.getNegative(); // -Dsun.net.inetaddr.negative.ttl=10(启动参数) || networkaddress.cache.negative.ttl=10(配置文件)
}
} public Cache put(String host, InetAddress[] addresses) {
int policy = getPolicy();
if (policy == InetAddressCachePolicy.NEVER) { // 0:不缓存
return this;
}
if (policy != InetAddressCachePolicy.FOREVER) { // 非永久缓存:需要清除过期记录
LinkedList<String> expired = new LinkedList<>();
long now = System.currentTimeMillis();
for (String key : cache.keySet()) {
CacheEntry entry = cache.get(key);
if (entry.expiration >= 0 && entry.expiration < now) { // 当前记录已过期
expired.add(key);
} else { // 当前记录未过期
break; // 后续记录的插入时间肯定比当期记录晚
}
}
for (String key : expired) { // 清除过期记录
cache.remove(key);
}
}
long expiration;
if (policy == InetAddressCachePolicy.FOREVER) { // -1:永久缓存
expiration = -1;
} else { // 非永久缓存
expiration = System.currentTimeMillis() + (policy * 1000); // 过期时间:policy秒
}
CacheEntry entry = new CacheEntry(addresses, expiration);
cache.put(host, entry); // 添加记录
return this;
} public CacheEntry get(String host) {
int policy = getPolicy();
if (policy == InetAddressCachePolicy.NEVER) { // 一定没有缓存记录
return null;
}
CacheEntry entry = cache.get(host); // 获取记录
if (entry != null && policy != InetAddressCachePolicy.FOREVER) { // 非永久缓存
if (entry.expiration >= 0 && entry.expiration < System.currentTimeMillis()) { // 检查是否过期
cache.remove(host);
entry = null;
}
}
return entry;
}
}

2)getCachedAddresses

private static boolean addressCacheInit = false; // 缓存未初始化
static InetAddress[] unknown_array; // 域名解析失败时使用的地址:任意地址{0.0.0.0, 0.0.0.0} || {::, ::}
private static Cache addressCache = new Cache(Cache.Type.Positive); // 正面缓存
private static Cache negativeCache = new Cache(Cache.Type.Negative); // 负面缓存
private static final HashMap<String, Void> lookupTable = new HashMap<>(); private static InetAddress[] getCachedAddresses(String hostname) {
hostname = hostname.toLowerCase();
synchronized (addressCache) {
cacheInitIfNeeded();
CacheEntry entry = addressCache.get(hostname); // 在正面缓存中查找记录
if (entry == null) {
entry = negativeCache.get(hostname); // 在负面缓存中查找记录
}
if (entry != null) {
return entry.addresses; // return 缓存中记录的地址
}
}
return null; // 在缓存中未找到记录
} private static void cacheInitIfNeeded() {
assert Thread.holdsLock(addressCache);
if (addressCacheInit) {
return;
}
unknown_array = new InetAddress[1]; // 大小为1
unknown_array[0] = impl.anyLocalAddress(); // Inet4Address(0.0.0.0/0.0.0.0) || Inet6Address(::/::)
addressCache.put(impl.anyLocalAddress().getHostName(), unknown_array);
addressCacheInit = true;
}

3)getAddressesFromNameService

private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr) throws UnknownHostException {
... ... if ((addresses = checkLookupTable(host)) == null) { // 是否正在解析该host ? 等待
try {
for (NameService nameService : nameServices) {
try {
addresses = nameService.lookupAllHostAddr(host); // 向该域名服务请求域名解析
success = true;
break; // 只要有一个域名服务成功解析host则退出循环
} catch (UnknownHostException uhe) {
// 1. 若host = localhost,则addresses = [loopbackAdress] & 退出循环
// 2. 否则准备抛异常(addresses = unknown_array & success = false & 记录uhe) & 继续循环
... ...
}
}
// 若reqAddr不为空 && addresses中存在与reqAddr相等的InetAddress,则将该InetAddress挪到第一个位置
... ... cacheAddresses(host, addresses, success); // 缓存记录 // 域名解析失败则抛异常uhe
... ... } finally {
updateLookupTable(host); // 唤醒所有等待解析该host的线程
}
}
return addresses;
} private static InetAddress[] checkLookupTable(String host) {
synchronized (lookupTable) {
if (lookupTable.containsKey(host) == false) { // 其它线程是否也在请求解析该host
lookupTable.put(host, null); // 告知之后请求解析该host的线程:正在进行域名解析
return null; // return & 进行域名解析
}
while (lookupTable.containsKey(host)) { // 域名解析尚未完成
try {
lookupTable.wait(); // 等待
} catch (InterruptedException e) {
}
}
}
InetAddress[] addresses = getCachedAddresses(host); // 在缓存中查找记录
if (addresses == null) { // 在缓存中未找到记录(之前线程对host进行域名解析失败:addresses == unknown_array)
synchronized (lookupTable) {
lookupTable.put(host, null);
return null; // return & 继续进行域名解析
}
}
return addresses; // 在缓存中找到记录
} private static void cacheAddresses(String hostname, InetAddress[] addresses, boolean success) {
hostname = hostname.toLowerCase();
synchronized (addressCache) {
cacheInitIfNeeded();
if (success) { // 域名解析成功
addressCache.put(hostname, addresses); // 在正面缓存中记录
} else { // 域名解析失败
negativeCache.put(hostname, addresses); // 在负面缓存中记录
}
}
} private static void updateLookupTable(String host) {
synchronized (lookupTable) {
lookupTable.remove(host); // 告知所有等待解析该host的线程:域名解析完毕
lookupTable.notifyAll(); // 唤醒所有等待解析该host的线程
}
}

2. getByAddress

getByName会立刻请求解析域名为IP地址,而getByAddress不会立刻请求解析IP地址为域名,获取域名在调用getHostName时执行。

public static InetAddress getByAddress(byte[] addr) throws UnknownHostException {
return getByAddress(null, addr);
} public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException {
// host截子串:[host] -> host
... ...
if (addr != null) {
if (addr.length == Inet4Address.INADDRSZ) { // 4字节:IPv4地址
return new Inet4Address(host, addr);
} else if (addr.length == Inet6Address.INADDRSZ) { // 16字节:IPv6地址
byte[] newAddr = IPAddressUtil.convertFromIPv4MappedAddress(addr); // 转IPv4地址
if (newAddr != null) { // 转换成功
return new Inet4Address(host, newAddr);
} else { // 转换失败
return new Inet6Address(host, addr);
}
}
}
throw new UnknownHostException("addr is of illegal length");
}

getHostName

public String getHostName() {
return getHostName(true);
} String getHostName(boolean check) {
if (holder().getHostName() == null) {
holder().hostName = InetAddress.getHostFromNameService(this, check); // 根据IP地址请求域名
}
return holder().getHostName();
} private static String getHostFromNameService(InetAddress addr, boolean check) {
String host = null;
for (NameService nameService : nameServices) {
try {
host = nameService.getHostByAddr(addr.getAddress()); // 根据IP地址向该域名服务请求域名 ... ... // prevent spoofing???
InetAddress[] arr = InetAddress.getAllByName0(host, check); // 反向由域名获取InetAddress:可能将进行域名解析
boolean ok = false;
if(arr != null) {
for(int i = 0; !ok && i < arr.length; i++) {// 在arr中查找与addr(IP地址)相等的InetAddress
ok = addr.equals(arr[i]); // InetAddress.equals:IP地址相等
}
}
if (!ok) { // arr == null || 在arr中未找到与addr(IP地址)相等的InetAddress
host = addr.getHostAddress(); // IP地址字符串
return host;
}
break;
} catch (SecurityException e) {
host = addr.getHostAddress(); // IP地址字符串
break;
} catch (UnknownHostException e) {
host = addr.getHostAddress(); // IP地址字符串
}
}
return host;
}

InetAddress之域名解析的更多相关文章

  1. JAVA学习第五十九课 — 网络编程概述

    网络模型 OSI(Open System Interconnection)开放系统互连:參考模型 TCP/IP 网络通讯要素 IP地址 port号 传输协议 网络參考模型 七层OSI模型的基本概念要了 ...

  2. poptest老李谈Socket

    poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...

  3. socket系列之什么是socket

    1.什么是socket Socket是应用层与TCP/IP协议族通信的中间抽象层,它是一组接口,应用层通过调用这些接口实现发送和接收数据.一般这种抽象层由操作系统提供或者由JVM自己实现.使用sock ...

  4. Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成

    一.前言 谈到优化,首先第一步,肯定是把一个大功能,拆分成一个个细小的环节,再单个拎出来找到可以优化的点,App 的网络优化也是如此. 在 App 访问网络的时候,DNS 解析是网络请求的第一步,默认 ...

  5. Java中的IP对象以及本地域名解析

    本地域名解析操作步骤: 1.打开C:\WINDOWS\system32\drivers\etc目录 2.找到host文件,用记事本打开 3.添加“空间IP  域名” package WebProgra ...

  6. InetAddress类和InetSocketAddress类

    InetAddress 类 封装计算机的 IP 地址,不包含端口号 InetAddress 类常用的方法 1 String getHostAddress() 获得 IP 地址 2 String get ...

  7. javase的网络编程(InetAddress,UDP,TCP,URL,Socket,DatagramSocket)

    通过一段时间对java网络编程相关内容的学习,写下这篇随笔,对这一部分的知识进行梳理和总结. 网络编程 一.网络编程三要素: IP地址:网络会给每个联网的主机分配一个数字的编码地址,该地址就是IP地址 ...

  8. WEB请求过程(http解析,浏览器缓存机制,域名解析,cdn分发)

    概述 发起一个http请求的过程就是建立一个socket通信的过程. 我们可以模仿浏览器发起http请求,譬如用httpclient工具包,curl命令等方式. curl "http://w ...

  9. [Web] Web请求过程之二:DNS 域名解析

    一.域名解析过程 1.浏览器检查缓存,域名缓存时间可以通过 TTL 属性来设置. 2.如果浏览器缓存中没有,浏览器会去查找操作系统缓存中是否有这个域名对应的 DNS 解析结果.如果要设置域名对应的 I ...

随机推荐

  1. Fiddler--的一些使用技巧

    1.Filters请求与响应的会话过滤 请求会话列表中存在上百个请求,怎么过滤想要的,可以启用 Fiddler  Filters强大的过滤机制,还可以依据正则来过滤,如: REGEX:(?insx). ...

  2. bootstrap-select属性

    # 参考:https://blog.csdn.net/zxl_langya/article/details/79247307 # bootstrap-select属性: <select mult ...

  3. 【Python学习】Jupyter解决单个变量输出问题

    使用Jupyter的时候有时候发现,我明明写了好几个变量打印,但是它只显示最后一个.Out只有一个. 但是使用下面的语句.就可以实现多个输出. from IPython.core.interactiv ...

  4. [session篇]看源码学习session(一)

    假如你是使用过或学习过PHP,你一定觉得很简单.session只不过是$_SESSION就可以搞得,这还不简单只是对一个key-value就能工作了.我觉得可以大多数的phper都是这样的,这是语言本 ...

  5. 2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6152 Friend-Graph 暴暴暴暴力

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6152 题意:判定一个无向图是否有三个点的团或者三个点的独立集. 解法:Ramsey theorem,n ...

  6. [ Python ] set集合及函数的使用

    1. set类型 set 和 dict 类似,也是一组 key 的集合,但是不存储 value. 由于 key  不重复,所以,在 set 中, 没有重复的 key 集合是可变类型 (1)集合的创建 ...

  7. layui文件单文件和多文件的上传、预览以及删除和修改

    活不多说,直接上代码 单文件上传 1.HTML <blockquote class="layui-elem-quote layui-quote-nm" style=" ...

  8. 你想了解的轮询、长轮询和websocket都在这里了

    日常生活中,有很多需要数据的实时更新,比如群聊信息的实时更新,还有投票系统的实时刷新等 实现的方式有很多种,比如轮询.长轮询.websocket 轮询 轮询是通过设置页面的刷新频率(设置多长时间自动刷 ...

  9. 常用python shell

    路径及文件操作 创建目录 os.mkdir(path_str) 列出当前文件夹中文件,存入string list中 os.listdir(path_str) 判断路径是否存在 os.path.exis ...

  10. PHP给图片加水印具体实现

    给图片加水印实现方法如下: class Mark { public function __construct() { } /** * 加水印 * @param file $srcImg 要加水印的图片 ...