Answers:
严格来说- hostname(1)
在Unix 上,您别无选择,只能调用或gethostname(2)
。这是您的计算机的名称。任何通过这样的IP地址来确定主机名的尝试
InetAddress.getLocalHost().getHostName()
在某些情况下注定会失败:
另外,请勿将IP地址的名称与主机名(主机名)混淆。隐喻可能更清楚:
有一个大城市(服务器),称为“伦敦”。在城墙内,发生了很多生意。这个城市有几个门(IP地址)。每个闸门都有一个名称(“北闸门”,“河闸门”,“南安普敦闸门” ...),但是闸门的名称不是城市的名称。同样,您无法通过使用大门的名称来推断城市的名称-“北门”将覆盖一半的较大城市,而不仅仅是一个城市。但是-一个陌生人(IP数据包)沿着河边走,问当地人:“我有一个陌生的地址:'Rivergate,第二左,第三宫'。你能帮我吗?” 当地人说:“当然,您在正确的道路上,只需继续前进,您将在半小时内到达目的地。”
我认为这几乎说明了这一点。
好消息是:通常不需要真实的主机名。在大多数情况下,任何解析为该主机上IP地址的名称都可以。(陌生人可能会通过北门(Northgate)进入该城市,但乐于助人的当地人会翻译“第二左”部分。)
如果还有其他情况,则必须使用此配置设置的确定来源-C函数gethostname(2)
。该函数也由程序调用hostname
。
InetAddress.getLocalHost().getHostName()
是更便携的方式。
exec("hostname")
实际上调用操作系统来执行hostname
命令。
这是关于SO的其他几个相关答案:
编辑:您应该查看AH的答案或Arnout Engelen的答案,以详细了解为什么这可能无法按预期工作的方式,具体取决于您的情况。作为对此特别要求便携式设备的人的答复,我仍然认为getHostName()
可以,但是他们提出了一些应考虑的优点。
InetAddress.getLocalHost()
在某些情况下,返回回送设备。在这种情况下,getHostName()
返回“ localhost”,这不是很有用。
正如其他人指出的那样,基于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,System.getenv("HOSTNAME")
如果您export HOSTNAME
在执行Java代码之前运行,则可在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
,在某些情况下,您可能不得不考虑它。
export HOSTNAME
在Ubuntu 14.04 LTS上运行Java代码之前,也会返回System.getenv("HOSTNAME")
。
export HOSTNAME
让Java使用它吗?我认为它应该是像PATH这样的默认环境变量。
\A
分隔符只是一种使用读取整个InputStream的技巧Scanner
。
InetAddress.getLocalHost().getHostName()
更好(如尼克解释),但仍然不是很好
一个主机可以用许多不同的主机名来识别。通常,您将在特定上下文中查找主机的主机名。
例如,在Web应用程序中,您可能正在寻找发出您当前正在处理的请求的人所使用的主机名。如何最好地找到一个取决于您用于Web应用程序的框架。
在某些其他面向Internet的服务中,您将希望可以从“外部”获得服务的主机名。由于代理服务器,防火墙等的原因,它甚至可能不是您安装服务的计算机上的主机名-您可以尝试使用一个合理的默认值,但无论安装它的人都应该将该配置设为可配置。
尽管已经回答了这个话题,但是还有更多话要说。
首先:显然,我们在这里需要一些定义。从网络的角度来看,这InetAddress.getLocalHost().getHostName()
为您提供了主机名。这种方法的问题已在其他答案中得到了很好的证明:它通常需要进行DNS查找,如果主机具有多个网络接口,则它是模棱两可的,有时甚至会完全失败(请参阅下文)。
但是在任何操作系统上,也都有另一个名字。在启动过程的很早就定义了主机的名称,很早就初始化了网络。Windows将其称为计算机名,Linux将其称为内核主机名,Solaris使用该词 nodename。我最喜欢“ 计算机名”一词,因此从现在开始我将使用该词。
在Linux / Unix上,计算机名是从C函数获得的gethostname()
,或者hostname
从HOSTNAME
类似于Bash的shell中的shell或环境变量命令。
在Windows上,计算机名是您从环境变量COMPUTERNAME
或Win32 GetComputerName
函数获得的名称。
Java无法获得我定义的“计算机名”。当然,还有其他答案中所述的解决方法,例如Windows调用System.getenv("COMPUTERNAME")
,但是在Unix / Linux上,如果不诉诸JNI / JNA或,就没有很好的解决方法Runtime.exec()
。如果您不介意JNI / JNA解决方案,则可以使用gethostname4j,它非常简单且易于使用。
让我们继续两个示例,一个来自Linux,一个来自Solaris,它们演示了如何轻松进入使用标准Java方法无法获得计算机名的情况。
在新创建的系统上,在安装过程中将主机命名为“芝加哥”,我们现在更改所谓的内核主机名:
$ 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。你基本上被困住了。无法检索值“达拉斯”或值“芝加哥”。
下面的示例基于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命令的结果将是:
$ hostname
dallas
就像在Linux示例中一样,调用InetAddress.getLocalHost().getHostName()
失败
java.net.UnknownHostException: dallas: dallas: node name or service name not known
就像Linux示例一样,您现在陷入困境。无法检索值“达拉斯”或值“芝加哥”。
很多时候,您会发现它InetAddress.getLocalHost().getHostName()
确实返回的值等于计算机名。这样就没有问题(除了增加名称解析的开销)。
该问题通常出现在PaaS环境中,在该环境中,计算机名和回送接口的名称之间存在差异。例如,人们报告了Amazon EC2中的问题。
一点搜索就显示了这个RFE报告:link1,link2。但是,从对该报告的评论来看,该问题似乎在很大程度上已被JDK团队误解,因此不太可能解决该问题。
我喜欢将RFE与其他编程语言进行比较。
环境变量也可能提供一种有用的方法- COMPUTERNAME
在Windows,HOSTNAME
大多数现代Unix / Linux外壳上。
参见: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;
StringUtils
它,并被Apache commons-lang项目提供的混淆了。强烈建议使用它或类似的东西。
System.getenv("HOSTNAME")
通过Mac上的Beanshell,以某种方式在Mac上显示为null,但可以将PATH
其提取出来。我也可以echo $HOSTNAME
在Mac上做,只是System.getenv(“ HOSTNAME”)似乎有问题。奇怪。
用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);
}
}
如果您不反对使用来自Maven Central的外部依赖关系,那么我写了gethostname4j自己解决了这个问题。它仅使用JNA调用libc的gethostname函数(或在Windows上获取ComputerName)并将其作为字符串返回给您。
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();
}
}
}
}
仅一线...跨平台(Windows-Linux-Unix-Mac(Unix))[始终有效,不需要DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
你完成了 !!