1. 域名解析的入口点
getaddrinfo
或getAllByName
方法:在底层,Java 使用getaddrinfo
(在 Unix-like 系统中)或类似的系统调用来解析域名。在 Java 的InetAddress
类中,getAllByName
方法是解析域名的入口点之一。它会调用getAddressesFromNameService
方法来获取域名对应的 IP 地址列表。
2. getAddressesFromNameService
方法
- 调用
nameService.lookupAllHostAddr
:此方法尝试通过注册的NameService
(如PlatformNameService
)来解析域名。PlatformNameService
是一个内部类,它实现了NameService
接口,并调用impl.lookupAllHostAddr
方法来执行实际的域名解析。 - 处理异常:如果解析失败,会抛出
UnknownHostException
。对于 "localhost" 这样的特殊情况,会直接返回回环地址。 - 处理请求地址:如果指定了请求地址 (
reqAddr
),则会检查解析结果中是否包含该地址,并进行相应的处理(如旋转数组).
3. lookupAllHostAddr
方法
- 本地缓存和系统缓存:在某些实现中,可能会先检查本地缓存或系统缓存,以快速获取域名对应的 IP 地址。
- 调用底层系统调用:最终,会调用底层的系统调用(如
getaddrinfo
)来从 DNS 服务器获取域名对应的 IP 地址列表。这个过程可能涉及与本地 DNS 服务器的通信,以及 DNS 服务器之间的查询转发.
4. plainConnect
和 plainConnect0
方法
- 权限检查:在
plainConnect
方法中,会先检查是否已经连接。如果未连接,则会进行权限检查,确保有权限连接到指定的 URL。 - 使用缓存:在
plainConnect0
方法中,会尝试从本地缓存中获取响应。如果缓存命中且满足条件(如不是 HTTPS 请求),则会直接使用缓存的响应,而不需要进行实际的网络连接。 - 代理选择:如果没有缓存命中,则会根据配置选择合适的代理(如实例代理或系统默认代理)。如果没有代理或代理连接失败,则会尝试直接连接。
- 创建 HTTP 客户端:通过
getNewHttpClient
方法创建一个新的 HTTP 客户端,并设置连接超时和读取超时等参数。 - 打开服务器连接:调用
openServer
方法来打开与远程服务器的连接。如果使用代理,则会通过代理服务器进行连接。
5. openServer
方法
- 安全检查:如果存在安全管理器,则会进行连接权限检查。
- 处理代理:如果使用代理,则会根据代理类型(如 HTTP 代理或 SOCKS 代理)进行相应的处理。对于 HTTP 代理,会调用
privilegedOpenServer
方法来通过代理服务器建立连接。 - 直接连接:如果没有代理或代理连接失败,则会直接连接到远程服务器。调用
super.openServer
方法来执行实际的连接操作.
总结
Java 客户端请求流程中解析域名的过程涉及多个层次的处理,从 InetAddress
类的高层方法到底层的系统调用。通过缓存、代理选择和权限检查等机制,Java 能够灵活地处理各种网络环境下的域名解析和连接请求。这个过程确保了客户端能够高效、安全地与远程服务器进行通信。
##实验代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUrlConnectionDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("http://www.baidu.com");
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法为GET
connection.setRequestMethod("GET");
// 设置超时时间
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 如果响应成功,则读取响应内容
if (responseCode == HttpURLConnection.HTTP_OK) { // 200表示成功
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
// 关闭流
in.close();
// 输出响应内容
System.out.println("Response Content: " + content.toString());
} else {
System.out.println("GET request not worked");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
##java源码
protected void plainConnect() throws IOException {
synchronized (this) {
if (connected) {
return;
}
}
SocketPermission p = URLtoSocketPermission(this.url);
if (p != null) {
try {
AccessController.doPrivilegedWithCombiner(
new PrivilegedExceptionAction<>() {
public Void run() throws IOException {
plainConnect0();
return null;
}
}, null, p
);
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}
} else {
// run without additional permission
plainConnect0();
}
}
protected void plainConnect0() throws IOException {
// try to see if request can be served from local cache
if (cacheHandler != null && getUseCaches()) {
try {
URI uri = ParseUtil.toURI(url);
if (uri != null) {
cachedResponse = cacheHandler.get(uri, getRequestMethod(), getUserSetHeaders().getHeaders());
if ("https".equalsIgnoreCase(uri.getScheme())
&& !(cachedResponse instanceof SecureCacheResponse)) {
cachedResponse = null;
}
if (logger.isLoggab