在Java中获取当前计算机主机名的最佳且最便携的方法是哪个?
Runtime.getRuntime().exec("hostname")
与
InetAddress.getLocalHost().getHostName()
在Java中获取当前计算机主机名的最佳且最便携的方法是哪个?
Runtime.getRuntime().exec("hostname")
与
InetAddress.getLocalHost().getHostName()
严格来说,你只能调用hostname(1)
或在Unix上调用gethostname(2)
来获取计算机的名称。这就是你的计算机名。任何试图通过IP地址来确定主机名的尝试都是无效的。
InetAddress.getLocalHost().getHostName()
在某些情况下,绑定将失败:
还不要混淆IP地址的名称和主机名(主机名)。比喻可能会更清晰:
有一个名为“伦敦”的大城市(服务器)。城市墙内发生了许多业务。城市有几个门(IP地址)。每个门都有一个名称(“North Gate”、“River Gate”、“Southampton Gate”...),但门的名称不是城市的名称。您也不能通过使用门的名称来推断城市的名称 - “North Gate”将捕获更大的城市的一半,而不仅仅是一个城市。但是 - 一个陌生人(IP数据包)沿着河流走,并问当地人:“我有一个奇怪的地址:‘Rivergate,第二个左,第三个房子’。你能帮我吗?”当地人说:“当然,你走在正确的路上,只需继续前进,你将在半小时内到达目的地。”
我认为这很清楚了。
好消息是:真正的主机名通常是不必要的。在大多数情况下,解析为此主机上的IP地址的任何名称都可以。 (陌生人可能通过Northgate进入城市,但乐于助人的当地人会翻译“第二个左转”部分。)
在剩下的角落情况中,您必须使用此配置设置的确定性源 - 这是C函数gethostname(2)
。该功能也由程序hostname
调用。
InetAddress.getLocalHost().getHostName()
是更可移植的方式。
exec("hostname")
实际上会调用操作系统来执行 hostname
命令。
以下是一些与此相关的答案:
编辑:你应该查看A.H.'s answer 或者 Arnout Engelen's answer 以获取关于为什么可能无法按预期工作的详细信息,这取决于您的情况。作为一个针对特定请求“可移植”的答案,我仍然认为getHostName()
是可以的,但他们提出了一些应该考虑的好建议。
InetAddress.getLocalHost()
返回的是回环设备。在这种情况下,getHostName()
会返回 "localhost",这并不是很有用。 - olenz正如其他人所指出的那样,基于DNS解析获取主机名是不可靠的。
由于这个问题在2018年仍然存在,我想分享一下我的网络独立解决方案,并在不同系统上进行一些测试运行。
以下代码尝试执行以下操作:
在Windows上
通过System.getenv()
读取COMPUTERNAME
环境变量。
执行hostname.exe
并读取响应。
在Linux上
通过System.getenv()
读取HOSTNAME
环境变量。
执行hostname
并读取响应。
读取/etc/hostname
(为了做到这一点,我正在执行cat
,因为代码片段已经包含了执行和读取的代码。不过,直接读取文件会更好)。
代码:
public static void main(String[] args) throws IOException {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
System.out.println("Windows computer name through env:\"" + System.getenv("COMPUTERNAME") + "\"");
System.out.println("Windows computer name through exec:\"" + execReadToString("hostname") + "\"");
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac os x")) {
System.out.println("Unix-like computer name through env:\"" + System.getenv("HOSTNAME") + "\"");
System.out.println("Unix-like computer name through exec:\"" + execReadToString("hostname") + "\"");
System.out.println("Unix-like computer name through /etc/hostname:\"" + execReadToString("cat /etc/hostname") + "\"");
}
}
public static String execReadToString(String execCommand) throws IOException {
try (Scanner s = new Scanner(Runtime.getRuntime().exec(execCommand).getInputStream()).useDelimiter("\\A")) {
return s.hasNext() ? s.next() : "";
}
}
不同操作系统的结果:
macOS 10.13.2
Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:""
OpenSUSE 13.1
Unix-like computer name through env:"machinename"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:""
Ubuntu 14.04 LTS
这个有点奇怪,因为echo $HOSTNAME
返回正确的主机名,但System.getenv("HOSTNAME")
却没有:
Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"
编辑:根据legolas108的说法,在执行Java代码之前在Ubuntu 14.04上运行export HOSTNAME
,System.getenv("HOSTNAME")
可以正常工作。
Windows 7
Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"
Windows 10
Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"
机器名已经更改,但我保留了大写和结构。注意执行 hostname
时会出现额外的换行符,在某些情况下您可能需要考虑它。
export HOSTNAME
命令,那么System.getenv("HOSTNAME")
也会返回相同的值。请注意,本句中涉及的所有英文词汇已经被正确翻译。 - legolas108\A
分隔符只是使用Scanner
读取整个InputStream的一种hack方法。 - MaltInetAddress.getLocalHost().getHostName()
是更好的选择(正如Nick所解释的),但仍不是非常好的选择。
一个主机可以有许多不同的主机名。通常情况下,您会寻找您的主机在特定上下文中使用的主机名。
例如,在Web应用程序中,您可能正在寻找发出当前处理请求的人使用的主机名。如何找到最佳主机名取决于您为Web应用程序使用哪个框架。
在某些其他面向互联网的服务中,您将需要从“外部”获得您的服务可用的主机名。由于代理,防火墙等原因,这甚至可能不是安装您的服务的计算机上的主机名 - 您可以尝试提供一个合理的默认值,但一定要让安装此服务的人员可以配置。
$ hostnamectl --static set-hostname dallas
现在内核的主机名是“dallas”,正如从主机名命令中可以看出:
$ hostname
dallas
但我们仍然拥有
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
这并没有错误配置。它只是意味着主机的网络名称(或者说回环接口的名称)与主机的计算机名称不同。
现在,尝试执行InetAddress.getLocalHost().getHostName()
,它会抛出java.net.UnknownHostException异常。你基本上被卡住了。无法检索到'dallas'或'chicago'的值。
以下示例基于Solaris 11.3。
主机已经有意地配置成环回名称 <> nodename。
换句话说,我们有:
$ svccfg -s system/identity:node listprop config
...
...
config/loopback astring chicago
config/nodename astring dallas
以及 /etc/hosts 文件的内容:
:1 chicago localhost
127.0.0.1 chicago localhost loghost
执行“hostname”命令的结果将是:
$ hostname
dallas
就像在 Linux 的例子中一样,调用 InetAddress.getLocalHost().getHostName()
将会失败。
java.net.UnknownHostException: dallas: dallas: node name or service name not known
就像Linux示例一样,你现在被卡住了。无法检索到值“dallas”和值“chicago”。
通常情况下,InetAddress.getLocalHost().getHostName()
确实会返回等于计算机名称的值。 因此没有问题(除了名称解析的额外开销)。
该问题通常出现在PaaS环境中,其中计算机名称与环回接口的名称不同。例如,人们报告Amazon EC2中出现问题。
一些搜索显示了此RFE报告:link1,link2。然而,根据该报告上的评论,JDK团队似乎大多数误解了这个问题,所以不太可能得到解决。
我喜欢这篇RFE中对其他编程语言的比较。
只需要一行代码即可实现跨平台(Windows-Linux-Unix-Mac(Unix))[始终可用,无需 DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
你完成了!!
Process p = Runtime.getRuntime().exec("hostname"); byte[] bytes = p.getInputStream().readAllBytes(); String hostname = new String(bytes,"ASCII").trim();
注意使用 .trim()
去除 hostname
(或者是 uname --nodename
)命令生成的多余 EOL。整个序列需要被 try/catch 包围。 - David Tonhofer环境变量也可能提供一个有用的方法--在Windows上是COMPUTERNAME
,在大多数现代Unix/Linux shell上是HOSTNAME
。
参见:https://dev59.com/4Wsz5IYBdhLWcg3weHgA#17956000
我将这些作为“补充”方法使用InetAddress.getLocalHost().getHostName()
,因为正如一些人所指出的那样,该函数并不适用于所有环境。
Runtime.getRuntime().exec("hostname")
也是另一种可能的补充方法。目前,我还没有使用它。
import java.net.InetAddress;
import java.net.UnknownHostException;
// try InetAddress.LocalHost first;
// NOTE -- InetAddress.getLocalHost().getHostName() will not work in certain environments.
try {
String result = InetAddress.getLocalHost().getHostName();
if (StringUtils.isNotEmpty( result))
return result;
} catch (UnknownHostException e) {
// failed; try alternate means.
}
// try environment properties.
//
String host = System.getenv("COMPUTERNAME");
if (host != null)
return host;
host = System.getenv("HOSTNAME");
if (host != null)
return host;
// undetermined.
return null;
StringUtils
感到困惑,它是由 Apache commons-lang 项目提供的。强烈建议使用它或类似的工具。 - JBCPSystem.getenv("HOSTNAME")
获取Mac主机名时出现了null值,但是PATH
却可以正常提取。我也可以在Mac上执行echo $HOSTNAME
,只是System.getenv("HOSTNAME")
似乎有问题。很奇怪。 - David在Java中获取当前计算机的主机名的最便携方式如下:
import java.net.InetAddress;
import java.net.UnknownHostException;
public class getHostName {
public static void main(String[] args) throws UnknownHostException {
InetAddress iAddress = InetAddress.getLocalHost();
String hostName = iAddress.getHostName();
//To get the Canonical host name
String canonicalHostName = iAddress.getCanonicalHostName();
System.out.println("HostName:" + hostName);
System.out.println("Canonical Host Name:" + canonicalHostName);
}
}
hostName == null;
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
{
while (interfaces.hasMoreElements()) {
NetworkInterface nic = interfaces.nextElement();
Enumeration<InetAddress> addresses = nic.getInetAddresses();
while (hostName == null && addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (!address.isLoopbackAddress()) {
hostName = address.getHostName();
}
}
}
}
hostname(1)
实际上调用了系统调用uname(3)
(在POSIX中定义),该调用可以在/usr/include/sys/utsname.h
中找到。(通过运行strace hostname
来尝试一下)。相应的命令是uname(1)
。因此,要获取所谓的“主机名”,实际上是“节点名”,也可以使用命令uname --nodename
。这通常是可以在/etc/hostname
中找到的字符串。 - David Tonhofer