Какое из следующего является лучшим и наиболее переносимым способом получить имя хоста текущего компьютера в Java?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Какое из следующего является лучшим и наиболее переносимым способом получить имя хоста текущего компьютера в Java?
Runtime.getRuntime().exec("hostname")
vs
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:
РЕДАКТИРОВАТЬ: Вы должны взглянуть на A.H. answer или Arnout Engelen answer, чтобы узнать, почему это может не работать должным образом, в зависимости от вашей ситуации. В качестве ответа для этого человека, который специально запросил перенос, я все еще думаю, что getHostName()
в порядке, но они приносят некоторые хорошие моменты, которые следует учитывать.
Как отмечали другие, получение имени хоста на основе разрешения DNS является ненадежным.
Поскольку этот вопрос, к сожалению, по-прежнему имеет значение в 2016, я хотел бы поделиться с вами своим сетевым решением, с некоторыми пробными запусками в разных системах.
Следующий код пытается сделать следующее:
В 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.indexOf("win") >= 0) {
System.out.println("Windows computer name throguh env:\"" + System.getenv("COMPUTERNAME") + "\"");
System.out.println("Windows computer name through exec:\"" + execReadToString("hostname") + "\"");
} else {
if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0) {
System.out.println("Linux computer name throguh env:\"" + System.getenv("HOSTNAME") + "\"");
System.out.println("Linux computer name through exec:\"" + execReadToString("hostname") + "\"");
System.out.println("Linux computer name through /etc/hostname:\"" + execReadToString("cat /etc/hostname") + "\"");
}
}
}
public static String execReadToString(String execCommand) throws IOException {
Process proc = Runtime.getRuntime().exec(execCommand);
try (InputStream stream = proc.getInputStream()) {
try (Scanner s = new Scanner(stream).useDelimiter("\\A")) {
return s.hasNext() ? s.next() : "";
}
}
}
Результаты для разных операционных систем:
OpenSuse 13.1
Linux computer name throguh env:"machinename"
Linux computer name through exec:"machinename
"
Linux computer name through /etc/hostname:""
Ubuntu 14.04 LTS
Это странно странно, поскольку echo $HOSTNAME
возвращает правильное имя хоста, но System.getenv("HOSTNAME")
не выполняет:
Linux computer name through env:"null"
Linux computer name through exec:"machinename
"
Linux 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
"
Имена машин были заменены, но я сохранил капитализацию и структуру. Обратите внимание на дополнительную строку новой строки при выполнении HOSTNAME
, возможно, вам придется учитывать ее в некоторых случаях.
InetAddress.getLocalHost().getHostName()
лучше (как объяснил Ник), но все же не очень хорошо
Один хост может быть известен под разными именами хостов. Обычно вы будете искать имя хоста вашего хоста в определенном контексте.
Например, в веб-приложении вы можете искать имя хоста, используемое тем, кто выдал запрос, который вы сейчас обрабатываете. Как лучше всего найти, что один зависит от того, какую структуру вы используете для своего веб-приложения.
В какой-либо другой службе, ориентированной на Интернет, вам нужно, чтобы имя вашего хоста было доступно через "снаружи". Из-за прокси-серверов, брандмауэров и т.д. Это может быть даже не имя хоста на компьютере, на котором установлена ваша служба, - вы можете попытаться найти разумный вариант по умолчанию, но вы должны определенно настроить это для тех, кто устанавливает это.
Переменные среды могут также предоставлять полезные средства - COMPUTERNAME
для Windows, HOSTNAME
для большинства современных оболочек Unix/Linux.
Смотрите: fooobar.com/questions/31446/...
Я использую их как "дополнительные" методы для 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;
Самый переносимый способ получить имя хоста текущего компьютера в 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);
}
}
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();
}
}
}
}
Хотя на эту тему уже был дан ответ, можно сказать больше.
Прежде всего: очевидно, что нам нужны некоторые определения. InetAddress.getLocalHost().getHostName()
дает имя хоста , как видно из сетевой перспективы. Проблемы с этим подходом хорошо документированы в других ответах: часто требуется поиск DNS, он неоднозначен, если хост имеет несколько сетевых интерфейсов, и он просто не работает иногда (см. Ниже).
Но на любой ОС есть и другое имя. Имя хоста, которое определяется очень рано в процессе загрузки, задолго до инициализации сети. Windows относится к этому как имя_компьютера, Linux называет его именем хоста ядра, а Solaris использует слово nodename. Мне больше всего нравится слово computername, поэтому я буду использовать это слово с этого момента.
В Linux/Unix имя_компьютера - это то, что вы получаете от команды 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.
В недавно созданной системе, где хост во время установки был назван "chicago" , теперь мы изменим так называемое имя хоста ядра:
$ hostnamectl --static set-hostname dallas
Теперь имя хоста ядра "dallas", как видно из команды hostname:
$ hostname
dallas
Но мы все еще имеем
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
В этом нет неправильной конфигурации. Это просто означает, что сетевое имя хоста (или, скорее, имя интерфейса loopback) отличается от имени компьютера узла.
Теперь попробуйте выполнить InetAddress.getLocalHost().getHostName()
, и он выкинет java.net.UnknownHostException. Вы в основном застреваете. Нет способа получить ни значение "даллас", ни значение "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 будет:
$ hostname
dallas
Как и в примере Linux, вызов InetAddress.getLocalHost().getHostName()
завершится с
java.net.UnknownHostException: dallas: dallas: node name or service name not known
Как и пример Linux, вы застряли. Нет способа получить ни значение "даллас", ни значение "chicago" .
Очень часто вы обнаружите, что InetAddress.getLocalHost().getHostName()
действительно вернет значение, равное имени пользователя. Таким образом, нет проблем (кроме дополнительных накладных расходов на разрешение имен).
Проблема возникает обычно в средах PaaS, где существует разница между именем пользователя и именем интерфейса loopback. Например, люди сообщают о проблемах в Amazon EC2.
Немного поиска показывает этот отчет RFE: link1, link2. Однако, судя по комментариям к этому отчету, проблема, по-видимому, была в значительной степени неверно понята командой JDK, поэтому маловероятно, что она будет рассмотрена.
Мне нравится сравнение в RFE с другими языками программирования.
Если вы не против использования внешней зависимости от центра maven, я написал gethostname4j, чтобы решить эту проблему для себя. Он просто использует JNA для вызова функции libc gethostname (или получает имя компьютера в Windows) и возвращает ее вам как строку.
InetAddress.getLocalHost(). getHostName() - лучший выход из двух, поскольку это лучшая абстракция на уровне разработчика.