建议在Java中获取主机名的方法

时间:2011-09-08 13:20:37

标签: java network-programming environment-variables hostname

以下哪项是获取Java当前计算机主机名的最佳和最便携方式?

Runtime.getRuntime().exec("hostname")

vs

InetAddress.getLocalHost().getHostName()

11 个答案:

答案 0 :(得分:303)

严格来说 - 你别无选择,只能在hostname(1)或 - 在gethostname(2)上调用。这是您的计算机的名称。是否尝试通过IP地址确定主机名,如

InetAddress.getLocalHost().getHostName()

在某些情况下肯定会失败:

  • IP地址可能无法解析为任何名称。错误的DNS设置,错误的系统设置或错误的提供商设置可能是原因。
  • DNS中的名称可以包含许多名为CNAME的别名。这些只能在一个方向上正确解决:名称到地址。反方向是模棱两可的。哪一个是“官方”名称?
  • 主机可以有许多不同的IP地址 - 每个地址可以有许多不同的名称。两种常见情况是:一个以太网端口有几个“逻辑”IP地址,或者计算机有几个以太网端口。它们是可共享的,无论它们共享IP还是具有不同的IP。这被称为“多宿主”。
  • DNS中的一个名称可以解析为多个IP地址。并非所有这些地址都必须位于同一台计算机上! (用例:一种简单的负载均衡形式)
  • 我们甚至不开始谈论动态IP地址。

另外,请勿将IP地址的名称与主机名(主机名)混淆。一个比喻可能会让它更清晰:

  

有一个叫做“伦敦”的大城市(服务器)。城墙内部发生了很多事情。这个城市有几个门(IP地址)。每个门都有一个名字(“北门”,“河门”,“南安普顿门”......),但门的名称不是城市的名称。此外,你不能通过使用门的名称来推断城市的名称 - “北门”将捕获一半的大城市,而不只是一个城市。然而 - 一个陌生人(IP包)沿着河边走来问当地人:“我有一个奇怪的地址:'Rivergate,第二个左边,第三个房子'。你能帮助我吗?”当地人说:“当然,你走在正确的道路上,只需前进,你将在半小时内到达目的地。”

这说明了我的想法。

好消息是:真正的主机名通常不是必需的。在大多数情况下,任何解析为此主机上的IP地址的名称都可以。 (陌生人可能会通过Northgate进入城市,但乐于助人的当地人会翻译“左二”部分。)

如果剩下的极端情况,您必须使用此配置设置的权威源 - 这是C函数gethostname(2)。该功能也由程序hostname调用。

答案 1 :(得分:89)

InetAddress.getLocalHost().getHostName()是更便携的方式。

exec("hostname")实际上调用操作系统来执行hostname命令。

以下是关于SO的其他几个相关答案:

编辑:您应该查看A.H.'s answerArnout Engelen's answer,了解可能无法按预期运行的详细信息,具体视具体情况而定。作为这个特别要求便携的人的答案,我仍然认为getHostName()很好,但是他们提出了一些应该考虑的好点。

答案 2 :(得分:47)

正如其他人所说,根据DNS解析获取主机名是不可靠的。

由于遗憾的是这个问题在 2018 中仍然存在,我想与您分享我的网络独立解决方案,并在不同的系统上进行一些测试。

以下代码尝试执行以下操作:

  • 在Windows上

    1. 通过COMPUTERNAME阅读System.getenv()环境变量。

    2. 执行hostname.exe并阅读回复

  • 在Linux上

    1. 通过HOSTNAME

    2. 阅读System.getenv()环境变量
    3. 执行hostname并阅读回复

    4. 阅读/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方法无法获得计算机名的情况。

Linux示例

在新创建的系统中,安装期间的主机被命名为'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示例

以下示例基于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中报告问题。

Bug / RFE报告

一些搜索结果显示此RFE报告:link1link2。但是,从对该报告的评论来看,这个问题似乎在很大程度上被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)并将其作为字符串返回给您。

https://github.com/mattsheppard/gethostname4j

答案 10 :(得分:-2)

InetAddress.getLocalHost()。getHostName()是两者中最好的方法,因为这是开发人员级别的最佳抽象。