Рекомендований спосіб отримання імені хоста на Java


274

Який із перелічених нижче є найкращим і портативним способом отримання імені хоста поточного комп’ютера на Java?

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

проти

InetAddress.getLocalHost().getHostName()


Який це стек технологій?
tom redfern

Я думаю, що єдине реальне резервне ім'я уніме (uts_name) від RMI / JMX VMID, але це конкретно для реалізації.
eckes

Відповіді:


336

Власне кажучи, у вас немає вибору, крім дзвінків hostname(1)або на Unix gethostname(2). Це ім’я вашого комп’ютера. Будь-яка спроба визначити ім'я хоста за такою IP-адресою

InetAddress.getLocalHost().getHostName()

може вийти з ладу за деяких обставин:

  • IP-адреса може не вирішити жодне ім’я. Причиною цього можуть бути погані налаштування DNS, погана налаштування системи або погана установка провайдера.
  • Ім'я в DNS може мати багато псевдонімів, які називаються CNAME. Їх можна вирішити лише в одному напрямку належним чином: ім'я для адреси. Зворотний напрямок неоднозначний. Яке з них - "офіційне" ім'я?
  • Хост може мати безліч різних IP-адрес - і кожна адреса може мати безліч різних імен. Два поширених випадки: один порт Ethernet має кілька "логічних" IP-адрес або комп'ютер має кілька портів Ethernet. Конфігурується, поділяють вони IP або мають різні IP-адреси. Це називається "багатоходом".
  • Одне ім’я в DNS може містити декілька IP-адрес. І не всі ці адреси повинні знаходитися на одному комп’ютері! (Usecase: проста форма збалансування навантаження)
  • Давайте навіть не будемо говорити про динамічні IP-адреси.

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

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

Це я дуже ілюструє.

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

Якщо в інших випадках кутових ви повинні використовувати остаточний джерело цієї настройки конфігурації - яка є функцією C gethostname(2). Ця функція також називається програмою hostname.


9
Не зовсім відповідь, на який я сподівався, але тепер я знаю, як поставити краще питання, дякую.
Сем Хаслер

3
Дивіться також stackoverflow.com/questions/6050011 / ...
Raedwald

4
Це приємне написання обмежень, які має будь-яке програмне забезпечення (не лише програма Java) при визначенні імені хоста у світі. Однак зауважте, що getHostName реалізований з точки зору базової ОС, імовірно, таким же чином, як і ім’я хоста / gethostname. У "нормальній" системі InetAddress.getLocalHost (). GetHostName () еквівалентно виклику імені хоста / gethostname, тому ви не можете реально сказати, що один виходить з ладу, а інший - ні.
Пітер Кардона

System.err.println (Runtime.getRuntime (). Exec ("ім'я хоста")); дає мені це: java.lang.UNIXProcess@6eb2384f
користувач152468

5
Реалізація InetAddress.getLocalHost (). GetHostName () насправді дуже детермінована :) Якщо ви дотримуєтеся вихідного коду, він врешті-решт викликає gethostname () у Windows та getaddrinfo () в Unixy системах. Результат такий же, як використання команди вашого імені хоста ОС. Тепер ім'я хоста може дати відповідь, яку ви не хочете використовувати, це можливо з багатьох причин. Як правило, програмне забезпечення повинно отримувати ім'я хоста від користувача у конфігураційному файлі, таким чином, це завжди правильне ім'я хоста. Ви можете використовувати InetAddress.getLocalhost (). GetHostName () як типовий, якщо користувач не надає значення.
Грег

93

InetAddress.getLocalHost().getHostName() є більш портативним способом.

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

Ось декілька інших відповідей на SO:

РЕДАКТУВАННЯ: Ви можете подивитися на відповідь AH або на відповідь Арнута Енгелена, щоб дізнатися, чому це може не працювати, як очікувалося, залежно від вашої ситуації. Як відповідь для цієї людини, яка спеціально попросила портативний, я все ще думаю, що getHostName()це добре, але вони підкреслюють кілька хороших моментів, які слід враховувати.


12
Запуск getHostName () кілька разів призводить до помилки. Наприклад, ось що я отримую в екземплярі Amazon EC2 AMI Linux: java.net.UnknownHostException: ім’я чи послуга невідомі java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez

1
Також InetAddress.getLocalHost()у деяких випадках повертає циклічний пристрій. У такому випадку getHostName()повертається "localhost", що не дуже корисно.
olenz

53

Як зазначали інші, отримання імені хоста на основі роздільної здатності 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() : "";
    }
}

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

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
"

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
"

Windows 10

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

Назви машин були замінені, але я зберегла капіталізацію та структуру. Зверніть увагу на додатковий новий рядок при виконанні hostname, можливо, вам доведеться враховувати його в деяких випадках.


3
Якщо export HOSTNAMEперед запуском коду Java на Ubuntu 14.04 LTS він також повертається System.getenv("HOSTNAME").
legolas108

Ви повинні явно, export HOSTNAMEщоб Java ним користувалася? Я думав, що це має бути env var за замовчуванням, як PATH.
Девід

2
Так, це один з помилок Java, який ніколи не буде виправлений з релігійних причин. Пов’язаний - мати можливість встановити ім'я процесу. Помилки реєстрували майже 20 років тому.
Tuntable

Відмінна відповідь, дуже ретельна! Я не знаю, чому ви використовуєте цей дивний роздільник, особливо враховуючи, що кожен друкований випуск має в ньому дивний новий рядок. Незважаючи на те, я оновлюю відповідь для роботи з MacOS, скорочую indexOf до вмісту викликів і закріплюю назву змінної ОС, щоб вона відповідала більше стандартним умовам імен змінних.
Чарлі

1
@Charlie Розмежувач - \Aце просто хак для читання всього InputStream за допомогою Scanner.
Солод

24

InetAddress.getLocalHost().getHostName() краще (як пояснив Нік), але все ж не дуже добре

Один хост може бути відомий під багатьма різними іменами хостів. Зазвичай ви шукаєте ім'я хоста, яке має ваш хост у конкретному контексті.

Наприклад, у веб-додатку ви можете шукати ім'я хоста, яким користується той, хто видав запит, яким ви зараз обробляєте. Як найкраще знайти це, залежить від того, яку рамку ви використовуєте для свого веб-додатку.

У будь-якому іншому сервісі, орієнтованому на Інтернет, ви хочете, щоб ім’я хоста, через яке ваша послуга доступна ззовні. Через проксі, брандмауери тощо це може бути навіть не ім'ям хоста на машині, на якій встановлено вашу службу - ви можете спробувати придумати розумний замовчування, але ви обов'язково повинні зробити це налаштованим для того, хто це встановлює.


18

Хоча на цю тему вже відповіли, можна сказати більше.

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

Але в будь-якій ОС також є інша назва. Ім'я хоста, яке визначається дуже рано в процесі завантаження, задовго до ініціалізації мережі. Windows називає це іменем комп'ютера , Linux називає це ім'ям хоста ядра, а Solaris використовує слово nodename . Мені найбільше подобається слово комп’ютерне ім’я , тому я буду використовувати це слово відтепер.

Пошук імені комп’ютера

  • У Linux / Unix ім'я комп'ютера - це те, що ви отримуєте від функції C gethostname(), або hostnameкоманди з оболонки або HOSTNAMEзмінної середовища в оболонках, схожих на Bash.

  • У Windows ім'я комп'ютера - це те, що ви отримуєте від змінної середовища COMPUTERNAMEабо GetComputerNameфункції Win32 .

У 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
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.

Хост свідомо налаштований так, що ім'я зворотного зв'язку <> ім'я вузла.

Іншими словами, ми маємо:

$ 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
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 з іншими мовами програмування.


12

Змінні середовища також можуть бути корисним засобом - 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
StringUtils? Що це?? (Я знаю, що ви маєте на увазі, я просто думаю, що це погана карма, щоб принести зовнішню бібліотеку з цією єдиною метою. І,
крім

23
Погано в кармі постійно писати "порожні" чеки вручну. Дуже багато проектів проводять такі перевірки вручну (як ви пропонуєте) та непослідовно, з великою кількістю помилок. Використовуйте бібліотеку.
Thomas W

15
Якщо хтось бачить це і насправді плутає StringUtils, його надає проект Apache commons-lang. Використовувати його чи щось подібне дуже рекомендується.
JBCP

Якось System.getenv("HOSTNAME")вийшов нуль на Mac через Beanshell для Java, але PATHбув вилучений нормально. Я також міг би робити і echo $HOSTNAMEна Mac, просто System.getenv ("HOSTNAME"), схоже, має проблему. Дивно.
Девід

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);
    }
}

8
Переносність в сторону, як цей метод порівнюється з методом у питанні? Які плюси / мінуси?
Маркес

4

Якщо ви не проти використання зовнішньої залежності від maven central, я написав gethostname4j, щоб вирішити цю проблему для себе. Він просто використовує JNA для виклику функції gethostname libc (або отримує ComputerName у Windows) і повертає її вам як рядок.

https://github.com/mattsheppard/gethostname4j


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();
            }
        }
    }
}

1
У чому користь цього методу?
Сем Хаслер

1
Це недійсне значення, воно дає лише ім'я першого NIC, який не є адаптером для зворотного зв'язку.
Стів-о

насправді це перший не циклічний зворотний зв'язок, який має ім'я хоста ... не обов'язково перший не циклічний зворотний зв'язок.
Адам Гент

1

Просто однолінійна ... кросова платформа (Windows-Linux-Unix-Mac (Unix)) [Завжди працює, DNS не потрібно]:

String hostname = new BufferedReader(
    new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
   .readLine();

Ви закінчили !!


InetAddress.getLocalHost (). GetHostName () не працюватиме в Unix?
P Satish Patro

1
Це працює, але вимагає дозволу dns, що робити, якщо ви підключені до Wi-Fi-зв’язку зі свого телефону (згадуючи лише один приклад), у нього не буде DNS, що знає локальну машину, і вона не працюватиме.
Дан Ортега

-2

InetAddress.getLocalHost (). GetHostName () - найкращий вихід з двох, оскільки це найкраща абстракція на рівні розробника.


Я шукаю відповідь, яка має справу з одним хостом, який знає багато імен хостів.
Сем Хаслер

@SamHasler, яка ваша конкретна ситуація? Як пояснив Арнут, існують різні способи залежно від того, що вам потрібно.
Нік Ноулсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.