用JAVA 源码角度看 客户端请求服务器流程中地址是域名情况下解析域名流程

server/2025/1/12 15:36:30/

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();// 设置请求方法为GETconnection.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 permissionplainConnect0();}}protected void plainConnect0()  throws IOException {// try to see if request can be served from local cacheif (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.isLoggable(PlatformLogger.Level.FINEST)) {logger.finest("Cache Request for " + uri + " / " + getRequestMethod());logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null"));}if (cachedResponse != null) {cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());cachedInputStream = cachedResponse.getBody();}}} catch (IOException ioex) {// ignore and commence normal connection}if (cachedHeaders != null && cachedInputStream != null) {connected = true;return;} else {cachedResponse = null;}}try {/* Try to open connections using the following scheme,* return on the first one that's successful:* 1) if (instProxy != null)*        connect to instProxy; raise exception if failed* 2) else use system default ProxySelector* 3) else make a direct connection if ProxySelector is not present*/if (instProxy == null) { // no instance Proxy is set/*** Do we have to use a proxy?*/ProxySelector sel =java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<>() {public ProxySelector run() {return ProxySelector.getDefault();}});if (sel != null) {URI uri = sun.net.www.ParseUtil.toURI(url);if (logger.isLoggable(PlatformLogger.Level.FINEST)) {logger.finest("ProxySelector Request for " + uri);}Iterator<Proxy> it = sel.select(uri).iterator();Proxy p;while (it.hasNext()) {p = it.next();try {if (!failedOnce) {http = getNewHttpClient(url, p, connectTimeout);http.setReadTimeout(readTimeout);} else {// make sure to construct new connection if first// attempt failedhttp = getNewHttpClient(url, p, connectTimeout, false);http.setReadTimeout(readTimeout);}if (logger.isLoggable(PlatformLogger.Level.FINEST)) {if (p != null) {logger.finest("Proxy used: " + p.toString());}}break;} catch (IOException ioex) {if (p != Proxy.NO_PROXY) {sel.connectFailed(uri, p.address(), ioex);if (!it.hasNext()) {if (logger.isLoggable(PlatformLogger.Level.FINEST)) {logger.finest("Retrying with proxy: " + p.toString());}http = getNewHttpClient(url, p, connectTimeout, false);http.setReadTimeout(readTimeout);break;}} else {throw ioex;}continue;}}} else {// No proxy selector, create http client with no proxyif (!failedOnce) {http = getNewHttpClient(url, null, connectTimeout);http.setReadTimeout(readTimeout);} else {// make sure to construct new connection if first// attempt failedhttp = getNewHttpClient(url, null, connectTimeout, false);http.setReadTimeout(readTimeout);}}} else {if (!failedOnce) {http = getNewHttpClient(url, instProxy, connectTimeout);http.setReadTimeout(readTimeout);} else {// make sure to construct new connection if first// attempt failedhttp = getNewHttpClient(url, instProxy, connectTimeout, false);http.setReadTimeout(readTimeout);}}ps = (PrintStream)http.getOutputStream();} catch (IOException e) {throw e;}// constructor to HTTP client calls openserverconnected = true;}protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)throws IOException {return HttpClient.New(url, p, connectTimeout, this);}protected synchronized void openServer() throws IOException {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkConnect(host, port);}if (keepingAlive) { // already openedreturn;}if (url.getProtocol().equals("http") ||url.getProtocol().equals("https") ) {if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {sun.net.www.URLConnection.setProxiedHost(host);privilegedOpenServer((InetSocketAddress) proxy.address());usingProxy = true;return;} else {// make direct connectionopenServer(host, port);usingProxy = false;return;}} else {/* we're opening some other kind of url, most likely an* ftp url.*/if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {sun.net.www.URLConnection.setProxiedHost(host);privilegedOpenServer((InetSocketAddress) proxy.address());usingProxy = true;return;} else {// make direct connectionsuper.openServer(host, port);usingProxy = false;return;}}}public void openServer(String server, int port) throws IOException {serverSocket = doConnect(server, port);try {OutputStream out = serverSocket.getOutputStream();if (capture != null) {out = new HttpCaptureOutputStream(out, capture);}serverOutput = new PrintStream(new BufferedOutputStream(out),false, encoding);} catch (UnsupportedEncodingException e) {throw new InternalError(encoding+" encoding not found", e);}serverSocket.setTcpNoDelay(true);}protected Socket doConnect (String server, int port)throws IOException, UnknownHostException {Socket s;if (proxy != null) {if (proxy.type() == Proxy.Type.SOCKS) {s = AccessController.doPrivileged(new PrivilegedAction<>() {public Socket run() {return new Socket(proxy);}});} else if (proxy.type() == Proxy.Type.DIRECT) {s = createSocket();} else {// Still connecting through a proxy// server & port will be the proxy address and ports = new Socket(Proxy.NO_PROXY);}} else {s = createSocket();}// Instance specific timeouts do have priority, that means// connectTimeout & readTimeout (-1 means not set)// Then global default timeouts// Then no timeout.if (connectTimeout >= 0) {s.connect(new InetSocketAddress(server, port), connectTimeout);} else {if (defaultConnectTimeout > 0) {s.connect(new InetSocketAddress(server, port), defaultConnectTimeout);} else {s.connect(new InetSocketAddress(server, port));}}if (readTimeout >= 0)s.setSoTimeout(readTimeout);else if (defaultSoTimeout > 0) {s.setSoTimeout(defaultSoTimeout);}return s;}public static InetAddress[] getAllByName(String host)throws UnknownHostException {return getAllByName(host, null);}private static InetAddress[] getAllByName(String host, InetAddress reqAddr)throws UnknownHostException {if (host == null || host.isEmpty()) {InetAddress[] ret = new InetAddress[1];ret[0] = impl.loopbackAddress();return ret;}validate(host);boolean ipv6Expected = false;if (host.charAt(0) == '[') {// This is supposed to be an IPv6 literalif (host.length() > 2 && host.charAt(host.length()-1) == ']') {host = host.substring(1, host.length() -1);ipv6Expected = true;} else {// This was supposed to be a IPv6 literal, but it's notthrow invalidIPv6LiteralException(host, false);}}// Check and try to parse host string as an IP address literalif (IPAddressUtil.digit(host.charAt(0), 16) != -1|| (host.charAt(0) == ':')) {byte[] addr = null;int numericZone = -1;String ifname = null;if (!ipv6Expected) {// check if it is IPv4 address only if host is not wrapped in '[]'try {addr = IPAddressUtil.validateNumericFormatV4(host);} catch (IllegalArgumentException iae) {var uhe = new UnknownHostException(host);uhe.initCause(iae);throw uhe;}}if (addr == null) {// Try to parse host string as an IPv6 literal// Check if a numeric or string zone id is present firstint pos;if ((pos = host.indexOf('%')) != -1) {numericZone = checkNumericZone(host);if (numericZone == -1) { /* remainder of string must be an ifname */ifname = host.substring(pos + 1);}}if ((addr = IPAddressUtil.textToNumericFormatV6(host)) == null &&(host.contains(":") || ipv6Expected)) {throw invalidIPv6LiteralException(host, ipv6Expected);}}if(addr != null) {InetAddress[] ret = new InetAddress[1];if (addr.length == Inet4Address.INADDRSZ) {if (numericZone != -1 || ifname != null) {// IPv4-mapped address must not contain zone-idthrow new UnknownHostException(host + ": invalid IPv4-mapped address");}ret[0] = new Inet4Address(null, addr);} else {if (ifname != null) {ret[0] = new Inet6Address(null, addr, ifname);} else {ret[0] = new Inet6Address(null, addr, numericZone);}}return ret;}} else if (ipv6Expected) {// We were expecting an IPv6 Literal since host string starts// and ends with square brackets, but we got something else.throw invalidIPv6LiteralException(host, true);}return getAllByName0(host, reqAddr, true, true);}static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)throws UnknownHostException{InetAddress[] addresses = null;UnknownHostException ex = null;validate(host);try {addresses = nameService.lookupAllHostAddr(host);} catch (UnknownHostException uhe) {if (host.equalsIgnoreCase("localhost")) {addresses = new InetAddress[] { impl.loopbackAddress() };}else {ex = uhe;}}if (addresses == null) {throw ex == null ? new UnknownHostException(host) : ex;}// More to do?if (reqAddr != null && addresses.length > 1 && !addresses[0].equals(reqAddr)) {// Find it?int i = 1;for (; i < addresses.length; i++) {if (addresses[i].equals(reqAddr)) {break;}}// Rotateif (i < addresses.length) {InetAddress tmp, tmp2 = reqAddr;for (int j = 0; j < i; j++) {tmp = addresses[j];addresses[j] = tmp2;tmp2 = tmp;}addresses[i] = tmp2;}}return addresses;}private static final class PlatformNameService implements NameService {public InetAddress[] lookupAllHostAddr(String host)throws UnknownHostException{return impl.lookupAllHostAddr(host);}public String getHostByAddr(byte[] addr)throws UnknownHostException{return impl.getHostByAddr(addr);}}public native InetAddress[] lookupAllHostAddr(String hostname)throws UnknownHostException;

##openjdk17 对应解析域名的  C++源码。error = getaddrinfo(hostname, NULL, &hints, &res);

/** Class:     java_net_Inet6AddressImpl* Method:    lookupAllHostAddr* Signature: (Ljava/lang/String;)[[B*/
JNIEXPORT jobjectArray JNICALL
Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,jstring host) {jobjectArray ret = NULL;const char *hostname;int error = 0;struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,*iterator;initInetAddressIDs(env);JNU_CHECK_EXCEPTION_RETURN(env, NULL);if (IS_NULL(host)) {JNU_ThrowNullPointerException(env, "host argument is null");return NULL;}hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);CHECK_NULL_RETURN(hostname, NULL);// try once, with our static buffermemset(&hints, 0, sizeof(hints));hints.ai_flags = AI_CANONNAME;hints.ai_family = AF_UNSPEC;error = getaddrinfo(hostname, NULL, &hints, &res);if (error) {
#if defined(MACOSX)// if getaddrinfo fails try getifaddrsret = lookupIfLocalhost(env, hostname, JNI_TRUE);if (ret != NULL || (*env)->ExceptionCheck(env)) {goto cleanupAndReturn;}
#endif// report errorNET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error);goto cleanupAndReturn;} else {int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0,inet6Index = 0, originalIndex = 0;int addressPreference =(*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);;iterator = res;while (iterator != NULL) {// skip duplicatesint skip = 0;struct addrinfo *iteratorNew = resNew;while (iteratorNew != NULL) {if (iterator->ai_family == iteratorNew->ai_family &&iterator->ai_addrlen == iteratorNew->ai_addrlen) {if (iteratorNew->ai_family == AF_INET) { /* AF_INET */struct sockaddr_in *addr1, *addr2;addr1 = (struct sockaddr_in *)iterator->ai_addr;addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {skip = 1;break;}} else {int t;struct sockaddr_in6 *addr1, *addr2;addr1 = (struct sockaddr_in6 *)iterator->ai_addr;addr2 = (struct sockaddr_in6 *)iteratorNew->ai_addr;for (t = 0; t < 16; t++) {if (addr1->sin6_addr.s6_addr[t] !=addr2->sin6_addr.s6_addr[t]) {break;}}if (t < 16) {iteratorNew = iteratorNew->ai_next;continue;} else {skip = 1;break;}}} else if (iterator->ai_family != AF_INET &&iterator->ai_family != AF_INET6) {// we can't handle other family typesskip = 1;break;}iteratorNew = iteratorNew->ai_next;}if (!skip) {struct addrinfo *next= (struct addrinfo *)malloc(sizeof(struct addrinfo));if (!next) {JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");ret = NULL;goto cleanupAndReturn;}memcpy(next, iterator, sizeof(struct addrinfo));next->ai_next = NULL;if (resNew == NULL) {resNew = next;} else {last->ai_next = next;}last = next;i++;if (iterator->ai_family == AF_INET) {inetCount++;} else if (iterator->ai_family == AF_INET6) {inet6Count++;}}iterator = iterator->ai_next;}// allocate array - at this point i contains the number of addressesret = (*env)->NewObjectArray(env, i, ia_class, NULL);if (IS_NULL(ret)) {/* we may have memory to free at the end of this */goto cleanupAndReturn;}if (addressPreference == java_net_InetAddress_PREFER_IPV6_VALUE) {inetIndex = inet6Count;inet6Index = 0;} else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {inetIndex = 0;inet6Index = inetCount;} else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {inetIndex = inet6Index = originalIndex = 0;}iterator = resNew;while (iterator != NULL) {if (iterator->ai_family == AF_INET) {jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);if (IS_NULL(iaObj)) {ret = NULL;goto cleanupAndReturn;}setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));if ((*env)->ExceptionCheck(env))goto cleanupAndReturn;setInetAddress_hostName(env, iaObj, host);if ((*env)->ExceptionCheck(env))goto cleanupAndReturn;(*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);inetIndex++;} else if (iterator->ai_family == AF_INET6) {jint scope = 0;jboolean ret1;jobject iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);if (IS_NULL(iaObj)) {ret = NULL;goto cleanupAndReturn;}ret1 = setInet6Address_ipaddress(env, iaObj, (char *)&(((struct sockaddr_in6*)iterator->ai_addr)->sin6_addr));if (ret1 == JNI_FALSE) {ret = NULL;goto cleanupAndReturn;}scope = ((struct sockaddr_in6 *)iterator->ai_addr)->sin6_scope_id;if (scope != 0) { // zero is default value, no need to setsetInet6Address_scopeid(env, iaObj, scope);}setInetAddress_hostName(env, iaObj, host);if ((*env)->ExceptionCheck(env))goto cleanupAndReturn;(*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);inet6Index++;}if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {originalIndex++;inetIndex = inet6Index = 0;}iterator = iterator->ai_next;}}
cleanupAndReturn:JNU_ReleaseStringPlatformChars(env, host, hostname);while (resNew != NULL) {last = resNew;resNew = resNew->ai_next;free(last);}if (res != NULL) {freeaddrinfo(res);}return ret;
}


http://www.ppmy.cn/server/157793.html

相关文章

跨域问题,开发

资料 1.【Vue】Access to XMLHttpRequest at XXX from origin XXX has been blocked by CORS policy&#xff08;Axios&#xff09;_uncaught runtime errors: 脳 error network error axi-CSDN博客 2.Vue | 基于 vue-admin-template 项目的跨域问题解决方法_vue-admin-templ…

Java一个简单的反弹动画练习

文章目录 说明代码详解创建窗体代码创建绘图板创建线程 运行结果完整代码 说明 做了一个小球和星型做反弹动画的窗体作为练习&#xff0c;分享给大家&#xff0c;为了方便和我一样的小白可以看的比较明白&#xff0c;所以尽量详细的标注了注释&#xff0c;希望能帮到同样在学习…

前端组件开发:组件开发 / 定义配置 / 配置驱动开发 / 爬虫配置 / 组件V2.0 / form表单 / table表单

一、最早的灵感 最早的灵感来自sprider / 网络爬虫 / 爬虫配置&#xff0c;在爬虫爬取网站文章时候&#xff0c;会输入给爬虫一个配置文件&#xff0c;里边的内容是一个json对象。里边包含了所有想要抓取的页面的信息。爬虫通过这个配置就可以抓取目标网站的数据。其实本文要引…

【Uniapp-Vue3】表单focus和blue事件的用法

focus就是input框聚焦时触发&#xff0c;blue就是input框失去焦点时触发。 分别用focus和blue触发事件。 下面这个例子中&#xff0c;就用focus和blur来改变Image的class样式。 触发函数时可以设置参数来获取输入框的值&#xff1a; 获取输入框的值就是e.detail.value 该案…

axios的替代方案onion-middleware

onion-middleware的由来 嗯。。。闲来无事瞎搞的&#xff01;&#xff01;&#xff01;&#xff01;主要用来实现请求/相应拦截&#xff0c;当然队列性的数据操作都是可以的 直接上使用教程 安装 npm install onion-middleware使用 import { OnionMiddleware } from onion…

MySQL 14 章——视图

一、常见的数据库对象 常见的数据库对象&#xff1a; 对象描述表(TABLE)表是存储数据的逻辑单元&#xff0c;以行和列的形式存在&#xff0c;列就是字段&#xff0c;行就是记录数据字典就是系统表&#xff0c;存放数据库相关信息的表。系统表的数据通常由数据库系统维护&#…

uniapp 使用 pinia 状态持久化

1.创建文件 stores -index.js -global.js2.对应文件内容 index.js 安装插件 npm i pinia-plugin-persistedstate import { createPinia } from pinia; import persist from pinia-plugin-persistedstate; const pinia createPinia(); pinia.use(persist); export default pi…

TR-069协议学习--Soap报文、事件、RPC方法

目录 一、SOAP报文 二、事件 三、RPC方法 四、错误码 一、SOAP报文 SOAP&#xff08;Simple Object Access Protocol&#xff0c;简单对象访问协议&#xff09;是一种基于XML的协议&#xff0c;用于在网络上交换结构化信息。SOAP报文是SOAP协议中用于传输数据的XML文档…