Рекомендуемый способ получить имя хоста в Java - PullRequest
250 голосов
/ 08 сентября 2011

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

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

против

InetAddress.getLocalHost().getHostName()

Ответы [ 10 ]

309 голосов
/ 18 октября 2011

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

InetAddress.getLocalHost().getHostName()

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

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

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

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

Думаю, это хорошо иллюстрирует.

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

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

89 голосов
/ 08 сентября 2011

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

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

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

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

49 голосов
/ 20 января 2015

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

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

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

  • В Windows

    1. Считайте переменную окружения COMPUTERNAME через System.getenv().

    2. Выполните hostname.exe и прочитайте ответ

  • В Linux

    1. Считайте переменную окружения HOSTNAME через System.getenv()

    2. Выполните hostname и прочитайте ответ

    3. Чтение /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() : "";
    }
}

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

маcOS 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") работает на Ubuntu 14.04, если вы запускаетеexport HOSTNAME перед выполнением кода Java.

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, в некоторых случаях вам, возможно, придется принять его во внимание.

23 голосов
/ 08 сентября 2011

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

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

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

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

11 голосов
/ 20 ноября 2016

Хотя на эту тему уже дан ответ, есть еще что сказать.

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

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

Поиск имени компьютера

  • В Linux / Unixcomputername - это то, что вы получаете из функции 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

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

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

Пример Solaris

Пример ниже основан на Solaris 11.3.

Хост имеетнамеренно настроен так, чтобы имя петлевого имени <> nodename.

Другими словами, у нас было:

$ 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, где существует разница между именем компьютера и именем интерфейса обратной связи.Например, люди сообщают о проблемах в Amazon EC2.

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

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

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

10 голосов
/ 27 декабря 2013

Переменные среды также могут быть полезными средствами - 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;
6 голосов
/ 04 марта 2013

Самый переносимый способ получить имя хоста текущего компьютера в 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);
    }
}
3 голосов
/ 12 апреля 2012
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();
            }
        }
    }
}
2 голосов
/ 08 января 2017

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

https://github.com/mattsheppard/gethostname4j

0 голосов
/ 13 октября 2011

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

...