Який із перелічених нижче є найкращим і портативним способом отримання імені хоста поточного комп’ютера на Java?
Runtime.getRuntime().exec("hostname")
проти
InetAddress.getLocalHost().getHostName()
Який із перелічених нижче є найкращим і портативним способом отримання імені хоста поточного комп’ютера на Java?
Runtime.getRuntime().exec("hostname")
проти
InetAddress.getLocalHost().getHostName()
Відповіді:
Власне кажучи, у вас немає вибору, крім дзвінків hostname(1)
або на Unix gethostname(2)
. Це ім’я вашого комп’ютера. Будь-яка спроба визначити ім'я хоста за такою IP-адресою
InetAddress.getLocalHost().getHostName()
може вийти з ладу за деяких обставин:
Також не плутайте ім'я IP-адреси з іменем хоста (ім'я хоста). Метафора може зробити це зрозумілішим:
Є велике місто (сервер) під назвою "Лондон". Всередині міських стін відбувається багато справ. У місті є кілька воріт (IP-адреси). Кожна брама має назву ("Північні ворота", "Річкові ворота", "Ворота Саутгемптона" ...), але назва воріт - це не назва міста. Крім того, ви не можете вивести назву міста, використовуючи назву воріт - "Північні ворота" охопили б половину великих міст, а не лише одне місто. Однак незнайомець (пакет IP) йде по річці і запитує місцевого: "У мене дивна адреса:" Рівергейт, другий лівий, третій будинок ". Чи можете ви мені допомогти?" Місцевий каже: "Звичайно, ви на правильній дорозі, просто продовжуйте, і ви прибудете до місця призначення протягом півгодини".
Це я дуже ілюструє.
Хороша новина: справжнє ім'я хоста зазвичай не потрібне. У більшості випадків будь-яке ім’я, яке перетворюється на IP-адресу цього хоста, буде робити. (Незнайомий чоловік може в'їхати в місто Нортгейтом, але корисні місцеві жителі перекладають частину "другої лівої".)
Якщо в інших випадках кутових ви повинні використовувати остаточний джерело цієї настройки конфігурації - яка є функцією C gethostname(2)
. Ця функція також називається програмою hostname
.
InetAddress.getLocalHost().getHostName()
є більш портативним способом.
exec("hostname")
фактично закликає операційну систему для виконання hostname
команди.
Ось декілька інших відповідей на SO:
РЕДАКТУВАННЯ: Ви можете подивитися на відповідь AH або на відповідь Арнута Енгелена, щоб дізнатися, чому це може не працювати, як очікувалося, залежно від вашої ситуації. Як відповідь для цієї людини, яка спеціально попросила портативний, я все ще думаю, що 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
"
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
, можливо, вам доведеться враховувати його в деяких випадках.
export HOSTNAME
перед запуском коду Java на Ubuntu 14.04 LTS він також повертається System.getenv("HOSTNAME")
.
export HOSTNAME
щоб Java ним користувалася? Я думав, що це має бути env var за замовчуванням, як PATH.
\A
це просто хак для читання всього InputStream за допомогою Scanner
.
InetAddress.getLocalHost().getHostName()
краще (як пояснив Нік), але все ж не дуже добре
Один хост може бути відомий під багатьма різними іменами хостів. Зазвичай ви шукаєте ім'я хоста, яке має ваш хост у конкретному контексті.
Наприклад, у веб-додатку ви можете шукати ім'я хоста, яким користується той, хто видав запит, яким ви зараз обробляєте. Як найкраще знайти це, залежить від того, яку рамку ви використовуєте для свого веб-додатку.
У будь-якому іншому сервісі, орієнтованому на Інтернет, ви хочете, щоб ім’я хоста, через яке ваша послуга доступна ззовні. Через проксі, брандмауери тощо це може бути навіть не ім'ям хоста на машині, на якій встановлено вашу службу - ви можете спробувати придумати розумний замовчування, але ви обов'язково повинні зробити це налаштованим для того, хто це встановлює.
Хоча на цю тему вже відповіли, можна сказати більше.
Насамперед: Очевидно, що тут нам потрібні деякі визначення. 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.
У новоствореній системі, де хост під час встановлення був названий як "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 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: 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 для Java, але 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 для виклику функції gethostname libc (або отримує ComputerName у Windows) і повертає її вам як рядок.
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();
Ви закінчили !!
InetAddress.getLocalHost (). GetHostName () - найкращий вихід з двох, оскільки це найкраща абстракція на рівні розробника.