在后台,为了获得 localhost 名称,SDK 对底层操作系统执行本机调用。
涉及的 C 函数是getLocalHostName
. 对于 IP 版本 4 和 6,您都可以找到合适的实现:基本上,如果您使用 IP 版本 6,它是相同的源代码,需要考虑的更改很少。
让我们假设例如 IP 版本 4 的代码。
对于 Java 11,相应的本机代码在Inet4AddressImpl.c中实现。这是如何getLocalHostname
实现的:
/*
* Class: java_net_Inet4AddressImpl
* Method: getLocalHostName
* Signature: ()Ljava/lang/String;
*/
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__)
// try to resolve hostname via nameservice
// if it is known but getnameinfo fails, hostname will still be the
// value from gethostname
struct addrinfo hints, *res;
// make sure string is null-terminated
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
// make sure string is null-terminated
hostname[NI_MAXHOST] = '\0';
#endif
}
return (*env)->NewStringUTF(env, hostname);
}
如您所见,当使用与 Solaris 不同的东西时,似乎代码只依赖于gethostname
获取所需的值。此限制是在此错误的上下文中在此提交中引入的。
在这里,您可以看到 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 /* defined(_ALLBSD_SOURCE) && !defined(HAS_GLIBC_GETHOSTBY_R) */
...
getLocalHostName
如果条件适用,则提供的实现会有所不同。
在我看来,在 Redhat 的情况下,该条件不适用,因此,以下代码是运行时使用的代码:
/************************************************************************
* Inet4AddressImpl
*/
/*
* Class: java_net_Inet4AddressImpl
* Method: getLocalHostName
* Signature: ()Ljava/lang/String;
*/
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))) {
/* Something went wrong, maybe networking is not setup? */
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) {/* host is known to name service */
getnameinfo(res->ai_addr,
res->ai_addrlen,
hostname,
NI_MAXHOST,
NULL,
0,
NI_NAMEREQD);
/* if getnameinfo fails hostname is still the value
from gethostname */
freeaddrinfo(res);
}
}
return (*env)->NewStringUTF(env, hostname);
}
如您所见,最后一个实现调用gethostname
也是最重要的,虽然间接地使用JVM_GetHostName
,包装在 C++ 代码中:
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
成功,getaddrinfo
则使用返回的主机名调用gethostname
. 如果反过来,最后一次调用成功,getnameinfo
则调用,并返回地址getaddrinfo
以获取最终主机名。
在某种程度上,这对我来说似乎很奇怪,我觉得我错过了一些东西,但这些差异很可能是你所经历的不同行为的原因;可以使用提供的本机代码测试假设并调试为您的系统获得的结果。