Какой из следующих способов является лучшим и наиболее переносимым способом получения имени хоста текущего компьютера в 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 () - лучший выход из двух, поскольку это лучшая абстракция на уровне разработчика.