在幕后,为了获取本地主机名,SDK执行了对底层操作系统的本地调用。
涉及的C函数是
getLocalHostName
。无论是IP版本4还是6,您都可以找到相应的实现:基本上它是相同的源代码,只需最少的更改即可考虑是否使用IP版本6。
例如,假设针对IP版本4的代码。
对于Java 11,相应的本地代码是在
Inet4AddressImpl.c中实现的。这就是
getLocalHostname
的实现方式:
JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
char hostname[NI_MAXHOST + 1];
hostname[0] = '\0';
if (gethostname(hostname, sizeof(hostname)) != 0) {
strcpy(hostname, "localhost");
} else {
#if defined(__solaris__)
struct addrinfo hints, *res;
hostname[NI_MAXHOST] = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
getnameinfo(res->ai_addr, res->ai_addrlen, hostname, sizeof(hostname),
NULL, 0, NI_NAMEREQD);
freeaddrinfo(res);
}
#else
hostname[NI_MAXHOST] = '\0';
#endif
}
return (*env)->NewStringUTF(env, hostname);
}
作为您可以看到的,当使用与Solaris不同的东西时,代码似乎只依赖于
gethostname
来获取所需的值。这个限制是在
this commit引入的,在
this bug的背景下。
这里您可以看到Java 8的类似IP 4版本本地源代码实现。
在那个源代码中,您可以找到与Java 11之前的源代码几个不同之处。
首先,代码分为两个部分,具体取决于以下定义是否适用:
#if defined(__GLIBC__) || (defined(__FreeBSD__) && (__FreeBSD_version >= 601104))
#define HAS_GLIBC_GETHOSTBY_R 1
#endif
#if defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R)
...
#else
...
如果条件成立或不成立,则getLocalHostName
的实现方式有所不同。
在我看来,在Redhat的情况下,条件不成立,因此以下代码是运行时使用的代码:
JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
char hostname[NI_MAXHOST+1];
hostname[0] = '\0';
if (JVM_GetHostName(hostname, sizeof(hostname))) {
strcpy(hostname, "localhost");
} else {
struct addrinfo hints, *res;
int error;
hostname[NI_MAXHOST] = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
error = getaddrinfo(hostname, NULL, &hints, &res);
if (error == 0) {
getnameinfo(res->ai_addr,
res->ai_addrlen,
hostname,
NI_MAXHOST,
NULL,
0,
NI_NAMEREQD);
freeaddrinfo(res);
}
}
return (*env)->NewStringUTF(env, hostname);
}
正如您所看到的,尽管是间接地使用
JVM_GetHostName
(
包含在C++代码中),但这最后一次实现仍然首先调用
gethostname
。
JVM_LEAF(int, JVM_GetHostName(char* name, int namelen))
JVMWrapper("JVM_GetHostName");
return os::get_host_name(name, namelen);
JVM_END
根据实际操作系统,os::get_host_name
将转换为不同的函数。对于linux,它将调用gethostname
:
inline int os::get_host_name(char* name, int namelen) {
return ::gethostname(name, namelen);
}
如果调用
gethostname
成功,则使用
gethostname
返回的主机名调用
getaddrinfo
。如果此调用成功,将使用
getaddrinfo
返回的地址调用
getnameinfo
以获取最终主机名。
在某种程度上,这似乎对我来说有些奇怪,我感觉我漏掉了什么,但这些差异很可能是您遇到的不同行为的原因;可以使用提供的本地代码并调试系统获得的结果来测试假设。