Рекомендуемый способ получить имя хоста в Java

Какое из следующего является лучшим и наиболее переносимым способом получить имя хоста текущего компьютера в Java?

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

vs

InetAddress.getLocalHost().getHostName()

Ответ 1

Строго говоря - у вас нет выбора, кроме вызова hostname(1) или - на Unix gethostname(2). Это имя вашего компьютера. Любая попытка определить имя хоста по IP-адресу, подобному этому

InetAddress.getLocalHost().getHostName()

в некоторых случаях будет терпеть неудачу:

  • IP-адрес может не разрешаться ни в одном имени. Причиной этого может быть неправильная настройка DNS, неправильная настройка системы или неправильная настройка провайдера.
  • Имя в DNS может иметь много псевдонимов, называемых CNAME. Они могут быть разрешены только в одном направлении: имя для адреса. Обратное направление неоднозначно. Какой из них является "официальным" именем?
  • Хост может иметь много разных IP-адресов, и каждый адрес может иметь много разных имен. Два распространенных случая: один Ethernet-порт имеет несколько "логических" IP-адресов или компьютер имеет несколько портов Ethernet. Он настраивается независимо от того, имеют ли они общий IP-адрес или имеют разные IP-адреса. Это называется "multihomed".
  • Одно имя в DNS может разрешить несколько IP-адресов. И не все эти адреса должны быть расположены на одном компьютере! (Usecase: простая форма балансировки нагрузки)
  • Позвольте даже не говорить о динамических IP-адресах.

Также не путайте имя IP-адреса с именем хоста (имя хоста). Метафора может сделать ее более ясной:

Существует большой город (сервер) под названием "Лондон". Внутри городских стен происходит много дел. В городе есть несколько ворот (IP-адреса). У каждого ворот есть имя ( "Северные ворота", "Речные ворота", "Саутгемптонские ворота"...), но имя ворот - это не название города. Также вы не можете вывести название города, используя имя ворот - "Северные ворота" поймают половину крупных городов, а не только одного города. Однако - незнакомец (IP-пакет) ходит по реке и спрашивает локального: "У меня странный адрес:" Ривергейт, второй слева, третий дом ". Можете ли вы мне помочь?" Местный говорит: "Конечно, вы на правильном пути, просто идите вперед, и вы прибудете в пункт назначения в течение получаса".

Это иллюстрирует это в значительной степени, я думаю.

Хорошая новость: реальное имя хоста обычно не требуется. В большинстве случаев будет выполняться любое имя, которое разрешает IP-адрес этого хоста. (Незнакомец может войти в город Нортгейтом, но полезные локальные жители переводят "вторую левую" часть.)

Если в остальных случаях вы должны использовать окончательный источник этого параметра конфигурации, который является функцией C gethostname(2). Эта функция также вызывается программой hostname.

Ответ 2

InetAddress.getLocalHost().getHostName() является более переносимым способом.

exec("hostname") фактически вызывает операционную систему для выполнения команды hostname.

Вот несколько других связанных ответов на SO:

РЕДАКТИРОВАТЬ: Вы должны взглянуть на A.H. answer или Arnout Engelen answer, чтобы узнать, почему это может не работать должным образом, в зависимости от вашей ситуации. В качестве ответа для этого человека, который специально запросил перенос, я все еще думаю, что getHostName() в порядке, но они приносят некоторые хорошие моменты, которые следует учитывать.

Ответ 3

Как отмечали другие, получение имени хоста на основе разрешения DNS является ненадежным.

Поскольку этот вопрос, к сожалению, по-прежнему имеет значение в 2016, я хотел бы поделиться с вами своим сетевым решением, с некоторыми пробными запусками в разных системах.

Следующий код пытается сделать следующее:

  • В 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.indexOf("win") >= 0) {
            System.out.println("Windows computer name throguh env:\"" + System.getenv("COMPUTERNAME") + "\"");
            System.out.println("Windows computer name through exec:\"" + execReadToString("hostname") + "\"");
        } else {
            if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0) {
                System.out.println("Linux computer name throguh env:\"" + System.getenv("HOSTNAME") + "\"");
                System.out.println("Linux computer name through exec:\"" + execReadToString("hostname") + "\"");
                System.out.println("Linux computer name through /etc/hostname:\"" + execReadToString("cat /etc/hostname") + "\"");
            }
        }
    }

    public static String execReadToString(String execCommand) throws IOException {
        Process proc = Runtime.getRuntime().exec(execCommand);
        try (InputStream stream = proc.getInputStream()) {
            try (Scanner s = new Scanner(stream).useDelimiter("\\A")) {
                return s.hasNext() ? s.next() : "";
            }
        }
    }

Результаты для разных операционных систем:

OpenSuse 13.1

Linux computer name throguh env:"machinename"
Linux computer name through exec:"machinename
"
Linux computer name through /etc/hostname:""

Ubuntu 14.04 LTS Это странно странно, поскольку echo $HOSTNAME возвращает правильное имя хоста, но System.getenv("HOSTNAME") не выполняет:

Linux computer name through env:"null"
Linux computer name through exec:"machinename
"
Linux computer name through /etc/hostname:"machinename
"

EDIT: В соответствии с legolas108, System.getenv("HOSTNAME") работает над Ubuntu 14.04, если вы запустите export HOSTNAME перед выполнением кода Java.

Windows 7

Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"

Имена машин были заменены, но я сохранил капитализацию и структуру. Обратите внимание на дополнительную строку новой строки при выполнении HOSTNAME, возможно, вам придется учитывать ее в некоторых случаях.

Ответ 4

InetAddress.getLocalHost().getHostName() лучше (как объяснил Ник), но все же не очень хорошо

Один хост может быть известен под разными именами хостов. Обычно вы будете искать имя хоста вашего хоста в определенном контексте.

Например, в веб-приложении вы можете искать имя хоста, используемое тем, кто выдал запрос, который вы сейчас обрабатываете. Как лучше всего найти, что один зависит от того, какую структуру вы используете для своего веб-приложения.

В какой-либо другой службе, ориентированной на Интернет, вам нужно, чтобы имя вашего хоста было доступно через "снаружи". Из-за прокси-серверов, брандмауэров и т.д. Это может быть даже не имя хоста на компьютере, на котором установлена ​​ваша служба, - вы можете попытаться найти разумный вариант по умолчанию, но вы должны определенно настроить это для тех, кто устанавливает это.

Ответ 5

Переменные среды могут также предоставлять полезные средства - COMPUTERNAME для Windows, HOSTNAME для большинства современных оболочек Unix/Linux.

Смотрите: fooobar.com/questions/31446/...

Я использую их как "дополнительные" методы для 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;

Ответ 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

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

Хотя на эту тему уже был дан ответ, можно сказать больше.

Прежде всего: очевидно, что нам нужны некоторые определения. InetAddress.getLocalHost().getHostName() дает имя хоста , как видно из сетевой перспективы. Проблемы с этим подходом хорошо документированы в других ответах: часто требуется поиск DNS, он неоднозначен, если хост имеет несколько сетевых интерфейсов, и он просто не работает иногда (см. Ниже).

Но на любой ОС есть и другое имя. Имя хоста, которое определяется очень рано в процессе загрузки, задолго до инициализации сети. Windows относится к этому как имя_компьютера, Linux называет его именем хоста ядра, а Solaris использует слово nodename. Мне больше всего нравится слово computername, поэтому я буду использовать это слово с этого момента.

Поиск имени пользователя

  • В Linux/Unix имя_компьютера - это то, что вы получаете от команды C функции gethostname() или hostname от оболочки или hostname переменной среды в Bash -подобных оболочках.

  • В 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

В этом нет неправильной конфигурации. Это просто означает, что сетевое имя хоста (или, скорее, имя интерфейса loopback) отличается от имени компьютера узла.

Теперь попробуйте выполнить InetAddress.getLocalHost().getHostName(), и он выкинет java.net.UnknownHostException. Вы в основном застреваете. Нет способа получить ни значение "даллас", ни значение "chicago" .

Пример Solaris

Пример ниже основан на 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, вы застряли. Нет способа получить ни значение "даллас", ни значение "chicago" .

Когда вы действительно будете бороться с этим?

Очень часто вы обнаружите, что InetAddress.getLocalHost().getHostName() действительно вернет значение, равное имени пользователя. Таким образом, нет проблем (кроме дополнительных накладных расходов на разрешение имен).

Проблема возникает обычно в средах PaaS, где существует разница между именем пользователя и именем интерфейса loopback. Например, люди сообщают о проблемах в Amazon EC2.

Отчеты об ошибках /RFE

Немного поиска показывает этот отчет RFE: link1, link2. Однако, судя по комментариям к этому отчету, проблема, по-видимому, была в значительной степени неверно понята командой JDK, поэтому маловероятно, что она будет рассмотрена.

Мне нравится сравнение в RFE с другими языками программирования.

Ответ 9

Если вы не против использования внешней зависимости от центра maven, я написал gethostname4j, чтобы решить эту проблему для себя. Он просто использует JNA для вызова функции libc gethostname (или получает имя компьютера в Windows) и возвращает ее вам как строку.

https://github.com/mattsheppard/gethostname4j

Ответ 10

InetAddress.getLocalHost(). getHostName() - лучший выход из двух, поскольку это лучшая абстракция на уровне разработчика.