Który z poniższych jest najlepszym i najbardziej przenośnym sposobem na uzyskanie nazwy hosta bieżącego komputera w Javie?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Który z poniższych jest najlepszym i najbardziej przenośnym sposobem na uzyskanie nazwy hosta bieżącego komputera w Javie?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Odpowiedzi:
Ściśle mówiąc - nie masz wyboru, musisz zadzwonić albo hostname(1)
- na Uniksie gethostname(2)
. To jest nazwa twojego komputera. Każda próba ustalenia nazwy hosta na podstawie adresu IP takiego jak ten
InetAddress.getLocalHost().getHostName()
w niektórych okolicznościach może się nie powieść:
Nie należy także mylić nazwy adresu IP z nazwą hosta (nazwa hosta). Metafora może to wyjaśnić:
Istnieje duże miasto (serwer) o nazwie „Londyn”. W obrębie murów miejskich dzieje się wiele interesów. Miasto ma kilka bram (adresy IP). Każda brama ma nazwę („North Gate”, „River Gate”, „Southampton Gate” ...), ale nazwa bramy nie jest nazwą miasta. Nie można też wydedukować nazwy miasta na podstawie nazwy bramy - „Brama Północna” złapałaby połowę większych miast, a nie tylko jedno miasto. Jednak - nieznajomy (pakiet IP) idzie wzdłuż rzeki i pyta miejscowego: „Mam dziwny adres:„ Rivergate, drugi w lewo, trzeci dom ”. Czy możesz mi pomóc?” Miejscowy mówi: „Oczywiście jesteś na dobrej drodze, po prostu idź przed siebie, a dotrzesz do celu w ciągu pół godziny”.
Myślę, że to ilustruje.
Dobra wiadomość jest taka: prawdziwa nazwa hosta zwykle nie jest konieczna. W większości przypadków wystarczy nazwa, która zamienia się w adres IP na tym hoście. (Nieznajomy może wejść do miasta przez Northgate, ale pomocni miejscowi tłumaczą część „2. lewa”).
W przypadku pozostałych przypadkach narożnych należy użyć ostateczne źródło tego ustawienia konfiguracji - co jest funkcja C gethostname(2)
. Ta funkcja jest również wywoływana przez program hostname
.
InetAddress.getLocalHost().getHostName()
jest bardziej przenośnym sposobem.
exec("hostname")
faktycznie woła system operacyjny, aby wykonać hostname
polecenie.
Oto kilka innych powiązanych odpowiedzi na temat SO:
EDYCJA: Powinieneś spojrzeć na odpowiedź AH lub odpowiedź Arnouta Engelena, aby dowiedzieć się, dlaczego może to nie działać zgodnie z oczekiwaniami, w zależności od twojej sytuacji. W odpowiedzi dla tej osoby, która specjalnie poprosiła o urządzenie przenośne, nadal uważam, że getHostName()
jest w porządku, ale przynoszą one kilka dobrych punktów, które należy wziąć pod uwagę.
InetAddress.getLocalHost()
w niektórych przypadkach zwraca urządzenie sprzężenia zwrotnego. W takim przypadku getHostName()
zwraca „localhost”, co nie jest zbyt przydatne.
Jak zauważyli inni, uzyskanie nazwy hosta na podstawie rozpoznania DNS jest zawodne.
Ponieważ to pytanie jest niestety nadal aktualne w 2018 r. , Chciałbym podzielić się z Wami moim niezależnym od sieci rozwiązaniem z niektórymi testami przeprowadzanymi na różnych systemach.
Poniższy kod próbuje wykonać następujące czynności:
W systemie Windows
Przeczytaj COMPUTERNAME
zmienną środowiskową System.getenv()
.
Wykonaj hostname.exe
i przeczytaj odpowiedź
W systemie Linux
Przeczytaj HOSTNAME
zmienną środowiskowąSystem.getenv()
Wykonaj hostname
i przeczytaj odpowiedź
Przeczytaj /etc/hostname
(w tym celu wykonuję, cat
ponieważ fragment zawiera już kod do wykonania i odczytu. Lepiej byłoby po prostu odczytać plik).
Kod:
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() : "";
}
}
Wyniki dla różnych systemów operacyjnych:
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
Ten jest dość dziwny, ponieważ echo $HOSTNAME
zwraca prawidłową nazwę hosta, ale System.getenv("HOSTNAME")
nie:
Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"
EDYCJA: Według legolas108 , System.getenv("HOSTNAME")
działa na Ubuntu 14.04, jeśli uruchomisz export HOSTNAME
przed uruchomieniem kodu Java.
System 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
"
Nazwy komputerów zostały zastąpione, ale zachowałem wielkie litery i strukturę. Zwróć uwagę na dodatkowy znak nowej linii podczas wykonywania hostname
, w niektórych przypadkach może być konieczne wzięcie go pod uwagę.
export HOSTNAME
przed uruchomieniem kodu Java na Ubuntu 14.04 LTS jest on również zwracany przez System.getenv("HOSTNAME")
.
export HOSTNAME
aby Java mogła z niego korzystać? Myślałem, że powinna to być domyślna zmienna env jak PATH.
\A
ogranicznik to tylko włamanie do odczytu całego InputStream za pomocą Scanner
.
InetAddress.getLocalHost().getHostName()
jest lepszy (jak wyjaśnia Nick), ale wciąż niezbyt dobry
Jeden host może być znany pod wieloma różnymi nazwami hostów. Zazwyczaj będziesz szukał nazwy hosta, którą host ma w określonym kontekście.
Na przykład w aplikacji internetowej możesz szukać nazwy hosta używanej przez osobę, która wysłała aktualnie obsługiwane żądanie. Jak najlepiej to znaleźć, zależy od tego, którego frameworku używasz w swojej aplikacji internetowej.
W jakiejś innej usłudze internetowej potrzebujesz nazwy hosta, za pośrednictwem której Twoja usługa jest dostępna z „zewnątrz”. Z powodu serwerów proxy, zapór ogniowych itp. Może to nawet nie być nazwa hosta na komputerze, na którym zainstalowana jest usługa - możesz spróbować znaleźć rozsądne ustawienie domyślne, ale zdecydowanie powinieneś skonfigurować tę opcję dla każdego, kto ją zainstaluje.
Chociaż na ten temat już udzielono odpowiedzi, jest więcej do powiedzenia.
Po pierwsze: najwyraźniej potrzebujemy tutaj kilku definicji. InetAddress.getLocalHost().getHostName()
Daje nazwę hosta, jak widać z perspektywy sieci . Problemy z tym podejściem są dobrze udokumentowane w innych odpowiedziach: często wymaga wyszukiwania DNS, niejednoznaczne jest, jeśli host ma wiele interfejsów sieciowych, a czasami po prostu czasami zawodzi (patrz poniżej).
Ale w każdym systemie operacyjnym jest też inna nazwa. Nazwa hosta, która jest definiowana na bardzo wczesnym etapie procesu rozruchu, na długo przed zainicjowaniem sieci. Windows określa to mianem nazwa_komputera , Linux nazywa ją nazwą hosta jądra, a Solaris używa słowa nazwa_węzła . Najbardziej podoba mi się słowo nazwa_komputera , więc odtąd będę go używać.
W Linux / Unix nazwa komputera jest tym, co otrzymujesz z funkcji C gethostname()
lub hostname
polecenia z powłoki lub HOSTNAME
zmiennej środowiskowej w powłokach typu Bash.
W systemie Windows nazwa komputera to nazwa zmiennej środowiskowej COMPUTERNAME
lub GetComputerName
funkcji Win32 .
Java nie ma możliwości uzyskania tego, co zdefiniowałem jako „nazwa komputera”. Oczywiście istnieją obejścia opisane w innych odpowiedziach, na przykład w przypadku wywoływania systemu Windows System.getenv("COMPUTERNAME")
, ale w systemach Unix / Linux nie można obejść tego problemu bez uciekania się do JNI / JNA lub Runtime.exec()
. Jeśli nie masz nic przeciwko rozwiązaniu JNI / JNA, istnieje gethostname4j, który jest bardzo prosty i bardzo łatwy w użyciu.
Przejdźmy do dwóch przykładów, jednego z Linuksa i jednego z Solaris, które pokazują, jak łatwo dostać się do sytuacji, w której nie można uzyskać nazwy komputera przy użyciu standardowych metod Java.
W nowo utworzonym systemie, w którym host podczas instalacji został nazwany „chicago”, zmieniamy teraz tak zwaną nazwę hosta jądra:
$ hostnamectl --static set-hostname dallas
Teraz nazwa hosta jądra to „dallas”, jak widać po poleceniu hostname:
$ hostname
dallas
Ale nadal mamy
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
Nie ma w tym żadnej błędnej konfiguracji. Oznacza to po prostu, że nazwa sieciowa hosta (a raczej nazwa interfejsu pętli zwrotnej) różni się od nazwy komputera hosta.
Teraz spróbuj wykonać, InetAddress.getLocalHost().getHostName()
a wygeneruje wyjątek java.net.UnknownHostException. W zasadzie utknąłeś. Nie ma możliwości odzyskania wartości „dallas” ani wartości „chicago”.
Poniższy przykład oparty jest na systemie Solaris 11.3.
Host został celowo skonfigurowany, aby nazwa pętli zwrotnej <> nazwa węzła.
Innymi słowy mamy:
$ svccfg -s system/identity:node listprop config
...
...
config/loopback astring chicago
config/nodename astring dallas
oraz zawartość / etc / hosts:
:1 chicago localhost
127.0.0.1 chicago localhost loghost
a wynikiem polecenia nazwy hosta byłoby:
$ hostname
dallas
Podobnie jak w przykładzie z Linuksem wywołanie InetAddress.getLocalHost().getHostName()
nie powiedzie się
java.net.UnknownHostException: dallas: dallas: node name or service name not known
Tak jak w przykładzie z Linuksem, który utknął. Nie ma możliwości odzyskania wartości „dallas” ani wartości „chicago”.
Bardzo często okazuje się, że InetAddress.getLocalHost().getHostName()
rzeczywiście zwróci wartość równą nazwie komputera. Więc nie ma problemu (z wyjątkiem dodatkowego obciążenia związanego z rozpoznawaniem nazw).
Problem pojawia się zwykle w środowiskach PaaS, w których istnieje różnica między nazwą komputera a nazwą interfejsu sprzężenia zwrotnego. Na przykład ludzie zgłaszają problemy w Amazon EC2.
Trochę wyszukiwania ujawnia ten raport RFE: link1 , link2 . Jednak sądząc po komentarzach do tego raportu, wydaje się, że problem został w dużej mierze źle zrozumiany przez zespół JDK, więc jest mało prawdopodobne, że zostanie rozwiązany.
Podoba mi się porównanie w RFE z innymi językami programowania.
Zmienne środowiskowe mogą również stanowić użyteczny środek - COMPUTERNAME
w systemie Windows, HOSTNAME
w większości nowoczesnych powłok Unix / Linux.
Zobacz: https://stackoverflow.com/a/17956000/768795
Używam ich jako „dodatkowych” metod InetAddress.getLocalHost().getHostName()
, ponieważ, jak zauważa kilka osób, ta funkcja nie działa we wszystkich środowiskach.
Runtime.getRuntime().exec("hostname")
jest kolejnym możliwym dodatkiem. Na tym etapie nie korzystałem z niego.
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
, zapewniono go w ramach projektu commons-lang Apache. Zaleca się korzystanie z niego lub czegoś podobnego.
System.getenv("HOSTNAME")
wyszedł na zero na Macu przez Beanshell dla Javy, ale PATH
został wyodrębniony. Mógłbym także zrobić echo $HOSTNAME
na komputerze Mac, tylko System.getenv („HOSTNAME”) wydaje się mieć problem. Dziwne.
Najbardziej przenośnym sposobem uzyskania nazwy hosta bieżącego komputera w Javie jest:
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);
}
}
Jeśli nie jesteś przeciwny używaniu zewnętrznej zależności od maven central, napisałem gethostname4j, aby rozwiązać ten problem dla siebie. Po prostu używa JNA do wywołania funkcji gethostname libc (lub pobiera ComputerName w systemie Windows) i zwraca ją jako ciąg znaków.
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();
}
}
}
}
Wystarczy jedna linijka ... na wielu platformach (Windows-Linux-Unix-Mac (Unix)) [Zawsze działa, nie wymaga DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
Jesteś skończony !!
InetAddress.getLocalHost (). GetHostName () to najlepsze wyjście z tych dwóch, ponieważ jest to najlepsza abstrakcja na poziomie programisty.