下面哪一种方法是在Java中获得当前计算机主机名的最佳和最可移植的方法?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
InetAddress.getLocalHost().getHostName()是更可移植的方式。
exec("hostname")实际上调用操作系统来执行hostname命令。
exec("hostname")
hostname
以下是关于SO的一些其他相关答案:
你应该看看A.H.的回答或Arnout Engelen的回答,以了解为什么这可能无法正常工作的详细信息,这取决于你的情况。作为对这个特别要求便携式的人的回答,我仍然认为getHostName()很好,但他们提出了一些应该考虑的优点。
getHostName()
InetAddress.getLocalHost().getHostName()更好(由Nick解释),但仍然不是很好
一个主机可以使用许多不同的主机名。通常,您将在特定的上下文中查找主机名。
例如,在web应用程序中,您可能要查找发出您当前正在处理的请求的人所使用的主机名。如何最好地找到它取决于你的web应用程序使用的框架。
在其他一些面向互联网的服务中,你会希望你的服务从“外部”可用的主机名。由于代理、防火墙等原因,这甚至可能不是安装服务的机器上的主机名——你可能会尝试提出一个合理的默认值,但你一定要让安装它的人都可以配置它。
InetAddress.getLocalHost(). gethostname()是两者中最好的方法,因为这是开发人员级别的最佳抽象。
严格地说-你没有选择,只能在Unix上调用hostname(1)或-。这是你电脑的名字。任何试图通过IP地址确定主机名的尝试,就像这样
hostname(1)
在某些情况下一定会失败:
另外,不要将ip地址的名称与主机的名称(hostname)混淆。打个比方可能会更清楚:
有一个大城市叫做“伦敦”。在城墙内,很多生意都在进行。城市有几个门(IP地址)。每扇门都有一个名字(“北门”、“河门”、“南安普顿门”),但门的名字并不是城市的名字。同样,你也不能用城门的名字来推断城市的名字——“北门”。能赶上一半的大城市,而不仅仅是一个城市。然而,一个陌生人(IP包)沿着河边走,问当地人:“我有一个奇怪的地址:‘河门,第二个左边,第三个房子’。”你能帮帮我吗?当地人说:“当然,你走对了,往前走,半小时内你就会到达目的地。”
我认为这很好地说明了这一点。
好消息是:真正的主机名是通常,这是不必要的。在大多数情况下,这台主机上任何解析为IP地址的名称都可以。(陌生人可能会从北门(Northgate)进入城市,但乐于助人的当地人会翻译“左二”;部分)。
在其余角落的情况下,您必须使用此配置设置的明确的源——即C函数gethostname(2)。该函数也由程序hostname调用。
gethostname(2)
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(); } } } }
在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); } }
环境变量也可以提供有用的方法——Windows上的COMPUTERNAME,大多数现代Unix/Linux shell上的HOSTNAME。
COMPUTERNAME
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;
正如其他人所指出的,根据DNS解析获取主机名是不可靠的。
不幸的是,由于这个问题在2018中仍然相关,我想与你分享我的网络独立解决方案,并在不同的系统上运行一些测试。
下面的代码尝试执行以下操作:
通过System.getenv()读取环境变量COMPUTERNAME。
System.getenv()
执行hostname.exe并读取响应
hostname.exe
通过System.getenv()读取环境变量HOSTNAME
执行hostname并读取响应
读取/etc/hostname(为了做到这一点,我正在执行cat,因为代码段已经包含要执行和读取的代码。不过,简单地阅读文件会更好)。
/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")没有:
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代码之前运行export HOSTNAME, System.getenv("HOSTNAME")可以在Ubuntu 14.04上工作。
export HOSTNAME
Windows 7
Windows computer name through env:"MACHINENAME" Windows computer name through exec:"machinename "
Windows 10
机器名称已被替换,但我保留了大写和结构。注意在执行hostname时额外的换行符,在某些情况下你可能不得不考虑它。
虽然这个话题已经有了答案,但还有更多要说的。
首先,显然我们需要一些定义。InetAddress.getLocalHost().getHostName()给出了主机的名称从网络的角度来看。这种方法的问题在其他答案中有很好的记录:它通常需要DNS查找,如果主机有多个网络接口,它是不明确的,有时它只是失败(见下文)。
但在任何操作系统上都有另一个名称。在引导过程的早期定义的主机名,远早于网络初始化。Windows将其称为computername, Linux将其称为内核主机名, Solaris使用节点名这个词。我最喜欢computername这个词,所以从现在开始我将使用这个词。
在Linux/Unix上,计算机名是从C函数gethostname()或shell中的hostname命令或Bash-like shell中的HOSTNAME环境变量中获得的。
gethostname()
在Windows上,计算机名是从环境变量COMPUTERNAME或Win32 GetComputerName函数中获得的。
GetComputerName
Java没有办法获得我定义的“计算机名”。当然,有其他答案中描述的变通办法,比如Windows调用System.getenv("COMPUTERNAME"),但在Unix/Linux上没有好的变通办法,除非求助于JNI/JNA或Runtime.exec()。如果你不介意JNI/JNA解决方案,那么有gethostname4j,它非常简单,非常容易使用。
System.getenv("COMPUTERNAME")
Runtime.exec()
让我们继续看两个例子,一个来自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。
主机已经过特意配置,使环回名称<>节点名。
换句话说我们有:
$ 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命令的结果是:
就像在Linux的例子中,调用InetAddress.getLocalHost().getHostName()会失败
java.net.UnknownHostException: dallas: dallas: node name or service name not known
就像Linux示例一样,您现在陷入了困境。没有办法检索值'dallas'或值'chicago'。
通常你会发现InetAddress.getLocalHost().getHostName()确实会返回一个等于计算机名的值。因此没有问题(除了名称解析的额外开销)。
这个问题通常出现在PaaS环境中,其中计算机名和环回接口名之间存在差异。例如,人们报告亚马逊EC2中的问题。
稍微搜索一下就发现了这个RFE报告:link1, link2。然而,从对该报告的评论来看,这个问题似乎在很大程度上被JDK团队误解了,所以它不太可能得到解决。
我喜欢RFE与其他编程语言的比较。
如果您不反对使用来自maven中心的外部依赖,我编写了gethostname4j来为自己解决这个问题。它只是使用JNA来调用libc的gethostname函数(或在Windows上获取ComputerName)并将其作为字符串返回给您。
https://github.com/mattsheppard/gethostname4j
只有一句话……跨平台(Windows-Linux-Unix-Mac(Unix))[始终工作,不需要DNS]:
String hostname = new BufferedReader( new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream())) .readLine();
你完蛋了!!
在Dan Ortega的回答是的基础上,我创建了一个通用的executeCommand(String)方法,它以command作为参数。
executeCommand(String)
command
import java.io.*; public class SystemUtil { public static void main(String[] args) throws IOException { System.out.println(retrieveHostName()); } public static String retrieveHostName() throws IOException { return executeCommand("hostname"); } private static String executeCommand(String command) throws IOException { return new BufferedReader( new InputStreamReader(Runtime.getRuntime().exec(command).getInputStream())) .readLine(); } }