以下哪项是获取Java当前计算机主机名的最佳和最便携方式?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
答案 0 :(得分:303)
严格来说 - 你别无选择,只能在hostname(1)
或 - 在gethostname(2)
上调用。这是您的计算机的名称。是否尝试通过IP地址确定主机名,如
InetAddress.getLocalHost().getHostName()
在某些情况下肯定会失败:
另外,请勿将IP地址的名称与主机名(主机名)混淆。一个比喻可能会让它更清晰:
有一个叫做“伦敦”的大城市(服务器)。城墙内部发生了很多事情。这个城市有几个门(IP地址)。每个门都有一个名字(“北门”,“河门”,“南安普顿门”......),但门的名称不是城市的名称。此外,你不能通过使用门的名称来推断城市的名称 - “北门”将捕获一半的大城市,而不只是一个城市。然而 - 一个陌生人(IP包)沿着河边走来问当地人:“我有一个奇怪的地址:'Rivergate,第二个左边,第三个房子'。你能帮助我吗?”当地人说:“当然,你走在正确的道路上,只需前进,你将在半小时内到达目的地。”
这说明了我的想法。
好消息是:真正的主机名通常不是必需的。在大多数情况下,任何解析为此主机上的IP地址的名称都可以。 (陌生人可能会通过Northgate进入城市,但乐于助人的当地人会翻译“左二”部分。)
如果剩下的极端情况,您必须使用此配置设置的权威源 - 这是C函数gethostname(2)
。该功能也由程序hostname
调用。
答案 1 :(得分:89)
InetAddress.getLocalHost().getHostName()
是更便携的方式。
exec("hostname")
实际上调用操作系统来执行hostname
命令。
以下是关于SO的其他几个相关答案:
编辑:您应该查看A.H.'s answer或Arnout Engelen's answer,了解可能无法按预期运行的详细信息,具体视具体情况而定。作为这个特别要求便携的人的答案,我仍然认为getHostName()
很好,但是他们提出了一些应该考虑的好点。
答案 2 :(得分:47)
正如其他人所说,根据DNS解析获取主机名是不可靠的。
由于遗憾的是这个问题在 2018 中仍然存在,我想与您分享我的网络独立解决方案,并在不同的系统上进行一些测试。
以下代码尝试执行以下操作:
在Windows上
通过COMPUTERNAME
阅读System.getenv()
环境变量。
执行hostname.exe
并阅读回复
在Linux上
通过HOSTNAME
System.getenv()
环境变量
执行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代码之前运行System.getenv("HOSTNAME")
,则export HOSTNAME
适用于Ubuntu 14.04。
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
时的额外换行符,在某些情况下可能需要考虑它。
答案 3 :(得分:23)
InetAddress.getLocalHost().getHostName()
更好(正如尼克所解释的那样),但仍然不是很好
一个主机可以在许多不同的主机名下知道。通常,您将在特定的上下文中查找主机所具有的主机名。
例如,在Web应用程序中,您可能正在查找发出您当前正在处理的请求的人使用的主机名。如何最好地找到一个取决于您为Web应用程序使用的框架。
在某种其他面向互联网的服务中,您需要通过“外部”提供您的服务的主机名。由于代理,防火墙等,这甚至可能不是您的服务所安装的计算机上的主机名 - 您可能会尝试提供合理的默认设置,但您绝对应该为安装此设备的人配置此设置。
答案 4 :(得分:10)
环境变量也可以在Windows上提供有用的方法 - COMPUTERNAME
,在大多数现代Unix / Linux shell上提供HOSTNAME
。
请参阅:https://stackoverflow.com/a/17956000/768795
我将这些用作"补充" 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;
答案 5 :(得分:10)
虽然已经回答了这个话题,但还有更多话要说。
首先:显然我们需要一些定义。从网络角度看,InetAddress.getLocalHost().getHostName()
为您提供了主机的名称。这种方法的问题在其他答案中得到了很好的记录:它通常需要DNS查找,如果主机有多个网络接口,它有点模糊,有时候很明显失败(见下文)。
但在任何操作系统上都有另一个名字。在网络初始化之前很久就在引导过程的早期定义的主机的名称。 Windows将其称为计算机名称,Linux将其称为内核主机名,Solaris使用单词 nodename 。我最喜欢 computername 这个词,所以我从现在开始使用这个词。
在Linux / Unix上,计算机名是从C函数gethostname()
或hostname
命令获得的,来自shell或{BAL-like shell中的HOSTNAME
环境变量。< / p>
在Windows上,计算机名是从环境变量COMPUTERNAME
或Win32 GetComputerName
函数中获得的。
Java无法获得我所定义的'计算机名'。当然,还有其他答案中描述的解决方法,例如Windows调用System.getenv("COMPUTERNAME")
,但是在Unix上/ Linux如果不采用JNI / JNA或Runtime.exec()
,没有好的解决方法。如果您不介意JNI / JNA解决方案,那么gethostname4j就变得简单易用了。
让我们继续两个例子,一个来自Linux,一个来自Solaris,它们演示了如何使用标准Java方法无法获得计算机名的情况。
在新创建的系统中,安装期间的主机被命名为'chicago',我们现在更改所谓的内核主机名:
$ hostnamectl --static set-hostname dallas
现在内核主机名是'dallas',从hostname命令可以看出:
$ 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。
故意配置主机,以便环回名称&lt;&gt;节点名。
换句话说,我们有:
$ 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环境中,其中computername和loopback接口的名称之间存在差异。例如,人们在Amazon EC2中报告问题。
一些搜索结果显示此RFE报告:link1,link2。但是,从对该报告的评论来看,这个问题似乎在很大程度上被JDK团队误解了,因此不太可能得到解决。
我喜欢RFE与其他编程语言的比较。
答案 6 :(得分:6)
以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);
}
}
答案 7 :(得分:3)
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();
}
}
}
}
答案 8 :(得分:3)
仅是一线式交叉平台(Windows-Linux-Unix-Mac(Unix))[始终有效,不需要DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
您完成了!!
答案 9 :(得分:2)
如果你不反对使用maven central的外部依赖,我写了gethostname4j来解决这个问题。它只是使用JNA调用libc的gethostname函数(或在Windows上获取ComputerName)并将其作为字符串返回给您。
答案 10 :(得分:-2)
InetAddress.getLocalHost()。getHostName()是两者中最好的方法,因为这是开发人员级别的最佳抽象。