Какой из следующих способов является лучшим и наиболее переносимым способом получения имени хоста текущего компьютера в 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-адреса с именем хоста (hostname). Метафора может прояснить это:
Существует большой город (сервер) под названием "Лондон". Внутри городских стен много дел происходит. В городе есть несколько ворот (IP-адреса). Каждые ворота имеют имя («Северные ворота», «Речные ворота», «Саутгемптонские ворота» ...), но название ворот не является названием города. Также вы не можете определить название города, используя название ворот - «Северные ворота» будут охватывать половину крупных городов, а не только один город. Однако незнакомец (IP-пакет) идет вдоль реки и спрашивает у местного жителя: «У меня странный адрес:« Ривергейт, второй слева, третий дом ». Вы можете мне помочь?» Местные жители говорят: «Конечно, вы на правильном пути, просто идите вперед, и вы прибудете к месту назначения в течение получаса».
Это иллюстрирует это в значительной степени, я думаю.
Хорошая новость: реальное имя хоста обычно не требуется. В большинстве случаев подойдет любое имя, которое разрешается в IP-адрес на этом хосте. (Незнакомец может войти в город через Нортгейт, но полезные местные жители переводят часть «2-й левый».)
Если в остальных случаях угловых вы должны использовать окончательный источник этой настройки конфигурации - которая является функцией C gethostname(2). Эта функция также вызывается программой hostname.
InetAddress.getLocalHost().getHostName() это более портативный способ.
exec("hostname")фактически вызывает операционную систему для выполнения hostnameкоманды.
Вот пара других связанных ответов на SO:
РЕДАКТИРОВАТЬ: Вам следует взглянуть на ответ AH или ответ Arnout Engelen, чтобы узнать, почему это может работать не так, как ожидалось, в зависимости от вашей ситуации. В качестве ответа для этого человека, который специально попросил портативное устройство, я все еще думаю, что getHostName()это хорошо, но они поднимают некоторые хорошие моменты, которые следует учитывать.
InetAddress.getLocalHost()в некоторых случаях возвращает устройство обратной петли. В этом случае getHostName()возвращает «localhost», что не очень полезно.
Как уже отмечалось, получение имени хоста на основе разрешения DNS ненадежно.
Поскольку этот вопрос, к сожалению, по-прежнему актуален в 2018 году , я хотел бы поделиться с вами своим независимым от сети решением, проведя несколько тестовых прогонов на разных системах.
Следующий код пытается сделать следующее:
На винде
Прочитайте COMPUTERNAMEпеременную среды через System.getenv().
Выполнить hostname.exeи прочитать ответ
В линуксе
Прочитайте 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
"
РЕДАКТИРОВАТЬ: Согласно 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 с помощью a Scanner.
InetAddress.getLocalHost().getHostName() лучше (как объяснил Ник), но все же не очень хорошо
Один хост может быть известен под разными именами хоста. Обычно вы будете искать имя вашего хоста в определенном контексте.
Например, в веб-приложении вы можете искать имя хоста, используемое тем, кто отправил запрос, который вы сейчас обрабатываете. Как лучше всего это найти, зависит от того, какую платформу вы используете для своего веб-приложения.
В какой-либо другой интернет-службе вам нужно, чтобы имя хоста, через которое доступна ваша служба, было «извне». Из-за прокси-серверов, брандмауэров и т. Д. Это может даже не быть именем хоста на машине, на которой установлена ваша служба - вы можете попытаться найти разумное значение по умолчанию, но вам обязательно следует настроить его для любого, кто его устанавливает.
Хотя на эту тему уже ответили, есть еще что сказать.
Прежде всего: очевидно, нам нужны некоторые определения здесь. Это InetAddress.getLocalHost().getHostName()дает вам имя хоста с точки зрения сети . Проблемы с этим подходом хорошо задокументированы в других ответах: он часто требует поиска DNS, он неоднозначен, если у хоста несколько сетевых интерфейсов, и иногда он просто выходит из строя (см. Ниже).
Но в любой ОС есть и другое имя. Имя хоста, которое определяется очень рано в процессе загрузки, задолго до инициализации сети. Windows называет это как имя компьютера , Linux называет его именем хоста ядра, а Solaris использует слово nodename . Мне больше всего нравится слово computername , так что теперь я буду использовать это слово.
В 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:
$ hostname
dallas
Но у нас все еще есть
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
Там нет неправильной конфигурации в этом. Это просто означает, что сетевое имя хоста (или, скорее, имя интерфейса обратной связи) отличается от имени компьютера хоста.
Теперь попробуйте выполнить, InetAddress.getLocalHost().getHostName() и он выдаст исключение java.net.UnknownHostException. Вы в основном застряли. Нет способа получить ни значение «Даллас», ни значение «Чикаго».
Пример ниже основан на 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, вы застряли. Нет способа получить ни значение «Даллас», ни значение «Чикаго».
Очень часто вы обнаружите, что 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 (или получает имя_компьютера в 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 () - лучший выход из двух, поскольку это лучшая абстракция на уровне разработчика.