提问人:Andy Knipp 提问时间:5/20/2020 最后编辑:Matt SheppardAndy Knipp 更新时间:10/6/2021 访问量:2126
InetAddress.getLocalHost().getHostName() JDK 11 和 JDK 8 之间的不同行为
InetAddress.getLocalHost().getHostName() different behavior between JDK 11 and JDK 8
问:
我编写了一个简单的 java 程序来运行:
System.out.println(InetAddress.getLocalHost().getHostName());
如果我在 RHEL 7.7 上编译它并在 Java 1.7.231 或 1.8.221 上运行它,它会返回 FQDN (computer.domain.com),但在同一服务器上,在 RHEL JDK 11.0.2 中编译它,它只返回服务器名称。
据我了解,它应该进行反向DNS查找(基本上是主机名-f),但是使用JDK 11,行为肯定不同。知道为什么会这样吗?
答:
这可能与此处报告的问题相同:InetAddress.getLocalhost() 在 java7 和 java8 中不会给出相同的结果。
它归结为 JDK 中的一个变化:
由于:http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/81987765cb81 被推送,我们调用 getaddrinfo / getnameinfo 来获取本地主机名,而不是较旧的(过时的)gethostbyname_r/gethostbyaddr_r 调用。
较新的调用遵循 localhosts /etc/nsswitch.conf 配置文件。对于此计算机,该文件告诉这些调用在引用其他命名服务之前查看文件。
由于 /etc/hosts 文件包含此主机名/IP 组合的显式映射,因此返回的内容。
在较旧的 JDK 中,gethostbyname_r实际上忽略了本地计算机设置,并立即委托给命名服务。
在后台,为了获取 localhost 名称,SDK 对底层操作系统执行本机调用。
涉及的 C 函数是 。对于 IP 版本 4 和 6,您可以找到适当的实现:基本上它是相同的源代码,如果您使用的是 IP 版本 6,则需要考虑最小的更改。getLocalHostName
例如,我们假设 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不同的东西时,代码似乎只依赖于获取所需的值。此限制是在此 bug 的上下文中在此提交中引入的。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);
}
正如你所看到的,最后一个实现调用也排在第一位,尽管是间接的,使用 ,包装在 C++ 代码中:gethostname
JVM_GetHostName
JVM_LEAF(int, JVM_GetHostName(char* name, int namelen))
JVMWrapper("JVM_GetHostName");
return os::get_host_name(name, namelen);
JVM_END
根据实际操作系统的不同,将转换为不同的功能。对于 linux,它将调用:os::get_host_name
gethostname
inline int os::get_host_name(char* name, int namelen) {
return ::gethostname(name, namelen);
}
如果调用成功,则使用 返回的主机名调用。反过来,如果最后一个调用成功,则调用地址以获取最终主机名。gethostname
getaddrinfo
gethostname
getnameinfo
getaddrinfo
在某种程度上,这对我来说似乎很奇怪,我觉得我错过了一些东西,但这些差异很可能是你所经历的不同行为的原因;可以使用提供的本机代码测试假设,并调试为系统获得的结果。
oracle文档中的这个答案可以帮助你:
在 Red Hat Linux 安装中,InetAddress.getLocalHost() 可能会返回一个对应于环回地址 (127.0.0.1) 的 InetAddress。这是因为默认安装会在 /etc/hosts 中创建计算机主机名和环回地址之间的关联。要确保 InetAddress.getLocalHost() 返回实际的主机地址,请在搜索主机之前更新 /etc/hosts 文件或名称服务配置文件 (/etc/nsswitch.conf) 以查询 dns 或 nis。
友情链接 : https://docs.oracle.com/javase/7/docs/technotes/guides/idl/jidlFAQ.html
JDK 1.7 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7166687 上的类似错误
在 JDK 11 上,localhostname 可能有一个内置的预定义 JDK 关键字,可以在检索 localhostname 时调用该关键字,并且您可能会使用您自己的变量调用来覆盖系统预定义关键字,在其中调用 localhostname,因为有时我们不小心用我们自己的用户定义的变量覆盖了内置变量,这会导致原始内置关键字丢失其值,该值在返回中显示为空或一些其他结果 这可能不是您问题的最佳答案,但我建议您应该查看 JDK 内置关键字和 RHEL linux 内置关键字,用于 Inet 调用以在结果中返回 localhostname
评论