Empfohlener Weg, um den Hostnamen in Java zu erhalten


274

Welche der folgenden Methoden ist die beste und portabelste Methode, um den Hostnamen des aktuellen Computers in Java abzurufen?

Runtime.getRuntime().exec("hostname")

vs.

InetAddress.getLocalHost().getHostName()


Welcher Technologie-Stack ist das?
Tom Redfern

Ich denke, der einzige echte uname (uts_name) gesicherte Name stammt von der RMI / JMX-VMID, aber dies ist implementierungsspezifisch.
eckes

Antworten:


336

Genau genommen haben Sie keine andere Wahl, als entweder hostname(1)oder unter Unix anzurufen gethostname(2). Dies ist der Name Ihres Computers. Jeder Versuch, den Hostnamen anhand einer solchen IP-Adresse zu ermitteln

InetAddress.getLocalHost().getHostName()

ist unter bestimmten Umständen zum Scheitern verurteilt:

  • Die IP-Adresse wird möglicherweise nicht in einen Namen aufgelöst. Ein schlechtes DNS-Setup, ein schlechtes System-Setup oder ein schlechtes Provider-Setup können der Grund dafür sein.
  • Ein Name in DNS kann viele Aliase haben, die als CNAMEs bezeichnet werden. Diese können nur in eine Richtung richtig aufgelöst werden: Name zu Adresse. Die umgekehrte Richtung ist nicht eindeutig. Welcher ist der "offizielle" Name?
  • Ein Host kann viele verschiedene IP-Adressen haben - und jede Adresse kann viele verschiedene Namen haben. Zwei häufige Fälle sind: Ein Ethernet-Port hat mehrere "logische" IP-Adressen oder der Computer hat mehrere Ethernet-Ports. Es ist konfigurierbar, ob sie eine IP gemeinsam nutzen oder unterschiedliche IPs haben. Dies wird als "Multihomed" bezeichnet.
  • Ein Name in DNS kann in mehrere IP-Adressen aufgelöst werden. Und nicht alle diese Adressen müssen sich auf demselben Computer befinden! (Usecase: Eine einfache Form des Lastausgleichs)
  • Lassen Sie uns nicht einmal über dynamische IP-Adressen sprechen.

Verwechseln Sie auch nicht den Namen einer IP-Adresse mit dem Namen des Hosts (Hostname). Eine Metapher könnte es klarer machen:

Es gibt eine große Stadt (Server) namens "London". Innerhalb der Stadtmauern passiert viel Geschäft. Die Stadt hat mehrere Tore (IP-Adressen). Jedes Tor hat einen Namen ("North Gate", "River Gate", "Southampton Gate" ...), aber der Name des Tors ist nicht der Name der Stadt. Sie können den Namen der Stadt auch nicht mit dem Namen eines Tors ableiten - "Nordtor" würde die Hälfte der größeren Städte und nicht nur eine Stadt erfassen. Ein Fremder (IP-Paket) geht den Fluss entlang und fragt einen Einheimischen: "Ich habe eine seltsame Adresse: 'Rivergate, zweites linkes, drittes Haus'. Können Sie mir helfen?" Der Einheimische sagt: "Natürlich sind Sie auf dem richtigen Weg, fahren Sie einfach fort und Sie werden innerhalb einer halben Stunde an Ihrem Ziel ankommen."

Das zeigt es ziemlich genau, denke ich.

Die gute Nachricht ist: Der echte Hostname ist normalerweise nicht erforderlich. In den meisten Fällen reicht jeder Name aus, der in eine IP-Adresse auf diesem Host aufgelöst wird. (Der Fremde könnte die Stadt über Northgate betreten, aber hilfreiche Einheimische übersetzen den Teil "2. links".)

In den verbleibenden Eckfällen müssen Sie die endgültige Quelle dieser Konfigurationseinstellung verwenden - dies ist die C-Funktion gethostname(2). Diese Funktion wird auch vom Programm aufgerufen hostname.


9
Nicht ganz die Antwort, auf die ich gehofft hatte, aber jetzt weiß ich, wie ich eine bessere Frage stellen kann, danke.
Sam Hasler


4
Das ist eine schöne Zusammenfassung der Einschränkungen, die jede Software (nicht nur ein Java-Programm) bei der Bestimmung des Hostnamens in der Welt hat. Beachten Sie jedoch, dass getHostName in Bezug auf das zugrunde liegende Betriebssystem implementiert ist, vermutlich genauso wie Hostname / gethostname. Auf einem "normalen" System entspricht InetAddress.getLocalHost (). GetHostName () dem Aufruf von hostname / gethostname, sodass Sie nicht wirklich sagen können, dass einer auf eine Weise fehlschlägt, die der andere nicht tut.
Peter Cardona

System.err.println (Runtime.getRuntime (). Exec ("Hostname")); gibt mir dies: java.lang.UNIXProcess@6eb2384f
user152468

5
Die Implementierung von InetAddress.getLocalHost (). GetHostName () ist tatsächlich sehr deterministisch :) Wenn Sie dem Quellcode folgen, werden letztendlich gethostname () unter Windows und getaddrinfo () auf Unixy-Systemen aufgerufen. Das Ergebnis entspricht der Verwendung des Befehls hostname des Betriebssystems. Jetzt kann der Hostname eine Antwort liefern, die Sie nicht verwenden möchten. Dies ist aus vielen Gründen möglich. Im Allgemeinen sollte die Software den Hostnamen vom Benutzer in einer Konfigurationsdatei erhalten. Auf diese Weise ist es immer der richtige Hostname. Sie können InetAddress.getLocalhost (). GetHostName () als Standard verwenden, wenn der Benutzer keinen Wert angibt.
Greg

93

InetAddress.getLocalHost().getHostName() ist der tragbarere Weg.

exec("hostname")ruft tatsächlich das Betriebssystem auf, um den hostnameBefehl auszuführen .

Hier sind einige andere verwandte Antworten auf SO:

BEARBEITEN: Sie sollten sich die Antwort von AH oder Arnout Engelen ansehen , um zu erfahren, warum dies je nach Ihrer Situation möglicherweise nicht wie erwartet funktioniert. Als Antwort für diese Person, die speziell tragbare Geräte angefordert hat, denke ich immer noch, dass dies getHostName()in Ordnung ist, aber sie bringt einige gute Punkte vor, die berücksichtigt werden sollten.


12
Das Ausführen von getHostName () führt manchmal zu einem Fehler. Beispiel: Folgendes erhalte ich in einer Amazon EC2 AMI Linux-Instanz: java.net.UnknownHostException: Name oder Dienst nicht bekannt java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez

1
Auch InetAddress.getLocalHost()in einigen Fällen gibt den Loopback - Device. In diesem Fall wird getHostName()"localhost" zurückgegeben, was nicht sehr nützlich ist.
Olenz

53

Wie andere angemerkt haben, ist es unzuverlässig, den Hostnamen basierend auf der DNS-Auflösung zu erhalten.

Da diese Frage 2018 leider immer noch relevant ist , möchte ich Ihnen meine netzwerkunabhängige Lösung mit einigen Testläufen auf verschiedenen Systemen vorstellen.

Der folgende Code versucht Folgendes zu tun:

  • Unter Windows

    1. Lesen Sie die COMPUTERNAMEUmgebungsvariable durch System.getenv().

    2. Führen hostname.exeSie die Antwort aus und lesen Sie sie

  • Unter Linux

    1. Lesen Sie die HOSTNAMEUmgebungsvariable durchSystem.getenv()

    2. Führen hostnameSie die Antwort aus und lesen Sie sie

    3. Lesen /etc/hostname(dazu führe ich aus, catda das Snippet bereits Code zum Ausführen und Lesen enthält. Es wäre jedoch besser, die Datei einfach zu lesen).

Der Code:

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() : "";
    }
}

Ergebnisse für verschiedene Betriebssysteme:

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 Dieser ist etwas seltsam, da er echo $HOSTNAMEden richtigen Hostnamen zurückgibt, aber System.getenv("HOSTNAME")nicht:

Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"

EDIT: Nach legolas108 , System.getenv("HOSTNAME")arbeitet auf Ubuntu 14.04 , wenn Sie ausführen , export HOSTNAMEbevor Sie den Java - Code ausgeführt wird .

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
"

Die Maschinennamen wurden ersetzt, aber ich habe die Groß- und Kleinschreibung und Struktur beibehalten. Beachten Sie den zusätzlichen Zeilenumbruch bei der Ausführung. hostnameIn einigen Fällen müssen Sie ihn möglicherweise berücksichtigen.


3
Wenn Sie export HOSTNAMEden Java-Code vor dem Ausführen unter Ubuntu 14.04 LTS ausführen, wird er auch von zurückgegeben System.getenv("HOSTNAME").
legolas108

Sie müssen explizit export HOSTNAMEfür Java verwenden, um es zu verwenden? Ich dachte, es sollte eine Standard-Umgebung wie PATH sein.
David

2
Ja, dies ist einer der Java-Fehler, die aus religiösen Gründen niemals behoben werden. Eine verwandte Möglichkeit besteht darin, den Prozessnamen festlegen zu können. Fehler, die vor fast 20 Jahren gemeldet wurden.
Tuntable

Hervorragende Antwort, sehr gründlich! Ich weiß nicht, warum Sie dieses seltsame Trennzeichen verwenden, besonders wenn man bedenkt, dass jede gedruckte Ausgabe eine seltsame neue Zeile enthält. Unabhängig davon aktualisiere ich die Antwort, um mit MacOS zu arbeiten, verkürze den indexOf auf Aufrufe und korrigiere den Namen der Betriebssystemvariablen so, dass er mit den Standardkonventionen für Variablennamen konsistenter ist.
Charlie

1
@Charlie das \ATrennzeichen ist nur ein Hack zum Lesen des gesamten InputStream mit a Scanner.
Malt

24

InetAddress.getLocalHost().getHostName() ist besser (wie von Nick erklärt), aber immer noch nicht sehr gut

Ein Host kann unter vielen verschiedenen Hostnamen bekannt sein. Normalerweise suchen Sie nach dem Hostnamen, den Ihr Host in einem bestimmten Kontext hat.

In einer Webanwendung suchen Sie möglicherweise nach dem Hostnamen, der von demjenigen verwendet wird, der die Anforderung ausgegeben hat, die Sie gerade bearbeiten. Wie Sie dieses am besten finden, hängt davon ab, welches Framework Sie für Ihre Webanwendung verwenden.

Bei einer anderen Internetverbindung möchten Sie, dass der Hostname, über den Ihr Dienst verfügbar ist, von außen verfügbar ist. Aufgrund von Proxys, Firewalls usw. ist dies möglicherweise nicht einmal ein Hostname auf dem Computer, auf dem Ihr Dienst installiert ist. Sie könnten versuchen, einen vernünftigen Standard festzulegen, aber Sie sollten diesen auf jeden Fall für jeden konfigurierbar machen, der diesen installiert.


18

Obwohl dieses Thema bereits beantwortet wurde, gibt es noch mehr zu sagen.

Zuallererst: Natürlich brauchen wir hier einige Definitionen. Das InetAddress.getLocalHost().getHostName()gibt Ihnen den Namen des Hosts aus der Netzwerkperspektive . Die Probleme mit diesem Ansatz sind in den anderen Antworten gut dokumentiert: Oft ist eine DNS-Suche erforderlich, es ist nicht eindeutig, ob der Host über mehrere Netzwerkschnittstellen verfügt, und manchmal schlägt dies einfach fehl (siehe unten).

Aber auf jedem Betriebssystem gibt es auch einen anderen Namen. Ein Name des Hosts, der sehr früh im Startvorgang definiert wird, lange bevor das Netzwerk initialisiert wird. Windows bezeichnet dies als Computernamen , Linux nennt es Kernel-Hostname und Solaris verwendet das Wort Knotenname . Das Wort Computername gefällt mir am besten , daher werde ich dieses Wort von nun an verwenden.

Den Computernamen finden

  • Unter Linux / Unix erhalten Sie den Computernamen von der C-Funktion gethostname()oder den hostnameBefehl von der Shell oder der HOSTNAMEUmgebungsvariablen in Bash-ähnlichen Shells.

  • Unter Windows erhalten Sie den Computernamen von der Umgebungsvariablen COMPUTERNAMEoder der Win32- GetComputerNameFunktion.

Java hat keine Möglichkeit, das zu erhalten, was ich als "Computername" definiert habe. Sicher, es gibt Problemumgehungen, wie in anderen Antworten beschrieben, wie zum Beispiel für Windows-Aufrufe System.getenv("COMPUTERNAME"), aber unter Unix / Linux gibt es keine gute Problemumgehung, ohne auf JNI / JNA oder zurückzugreifen Runtime.exec(). Wenn Ihnen eine JNI / JNA-Lösung nichts ausmacht, gibt es gethostname4j, das kinderleicht und sehr einfach zu bedienen ist.

Fahren wir mit zwei Beispielen fort, einem von Linux und einem von Solaris, die zeigen, wie Sie leicht in eine Situation geraten können, in der Sie den Computernamen nicht mit Standard-Java-Methoden erhalten können.

Linux Beispiel

Auf einem neu erstellten System, auf dem der Host während der Installation als "Chicago" bezeichnet wurde, ändern wir jetzt den sogenannten Kernel-Hostnamen:

$ hostnamectl --static set-hostname dallas

Jetzt lautet der Kernel-Hostname 'dallas', wie aus dem Befehl hostname hervorgeht:

$ hostname
dallas

Aber wir haben immer noch

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Darin liegt keine Fehlkonfiguration. Dies bedeutet lediglich, dass sich der Netzwerkname des Hosts (oder vielmehr der Name der Loopback-Schnittstelle) vom Computernamen des Hosts unterscheidet.

Versuchen Sie nun die Ausführung InetAddress.getLocalHost().getHostName() und es wird eine java.net.UnknownHostException ausgelöst. Sie stecken im Grunde fest. Es gibt keine Möglichkeit, weder den Wert 'dallas' noch den Wert 'chicago' abzurufen.

Solaris Beispiel

Das folgende Beispiel basiert auf Solaris 11.3.

Der Host wurde absichtlich so konfiguriert, dass der Loopback-Name <> Knotenname ist.

Mit anderen Worten haben wir:

$ svccfg -s system/identity:node listprop config
...
...
config/loopback             astring        chicago
config/nodename             astring        dallas

und den Inhalt von / etc / hosts:

:1 chicago localhost
127.0.0.1 chicago localhost loghost

und das Ergebnis des Befehls hostname wäre:

$ hostname
dallas

Genau wie im Linux-Beispiel schlägt ein Aufruf von InetAddress.getLocalHost().getHostName()fehl

java.net.UnknownHostException: dallas:  dallas: node name or service name not known

Genau wie im Linux-Beispiel stecken Sie jetzt fest. Es gibt keine Möglichkeit, weder den Wert 'dallas' noch den Wert 'chicago' abzurufen.

Wann wirst du wirklich damit kämpfen?

Sehr oft werden Sie feststellen, dass InetAddress.getLocalHost().getHostName()tatsächlich ein Wert zurückgegeben wird, der dem Computernamen entspricht. Es gibt also kein Problem (außer dem zusätzlichen Aufwand für die Namensauflösung).

Das Problem tritt normalerweise in PaaS-Umgebungen auf, in denen ein Unterschied zwischen dem Computernamen und dem Namen der Loopback-Schnittstelle besteht. Zum Beispiel melden Leute Probleme in Amazon EC2.

Fehler- / RFE-Berichte

Ein bisschen Suchen zeigt diesen RFE-Bericht: Link1 , Link2 . Nach den Kommentaren zu diesem Bericht zu urteilen, scheint das Problem jedoch vom JDK-Team weitgehend missverstanden worden zu sein, so dass es unwahrscheinlich ist, dass es behoben wird.

Ich mag den Vergleich in der RFE mit anderen Programmiersprachen.


11

Umgebungsvariablen können ebenfalls ein nützliches Mittel sein - COMPUTERNAMEunter Windows HOSTNAMEauf den meisten modernen Unix / Linux-Shells.

Siehe: https://stackoverflow.com/a/17956000/768795

Ich verwende diese als "ergänzende" Methoden InetAddress.getLocalHost().getHostName(), da diese Funktion, wie mehrere Leute betonen, nicht in allen Umgebungen funktioniert.

Runtime.getRuntime().exec("hostname")ist eine weitere mögliche Ergänzung. Zu diesem Zeitpunkt habe ich es noch nicht benutzt.

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;

6
StringUtils? Was ist das?? (Ich weiß, was du meinst. Ich denke nur, dass es schlechtes Karma ist, eine externe Bibliothek für diesen alleinigen Zweck
einzurichten. Und obendrein

23
Es ist schlechtes Karma, ständig "leere" Schecks manuell zu schreiben. Bei sehr vielen Projekten werden solche Überprüfungen manuell (wie von Ihnen vorgeschlagen) und inkonsistent durchgeführt, mit vielen daraus resultierenden Fehlern. Verwenden Sie eine Bibliothek.
Thomas W

15
Falls jemand dies sieht und tatsächlich verwirrt ist StringUtils, wird es vom Apache commons-lang-Projekt bereitgestellt. Die Verwendung oder ähnliches wird dringend empfohlen.
JBCP

Irgendwie System.getenv("HOSTNAME")kam auf dem Mac über Beanshell für Java null heraus, wurde aber PATHok extrahiert. Ich könnte es auch echo $HOSTNAMEauf einem Mac tun , nur System.getenv ("HOSTNAME") scheint ein Problem zu haben. Seltsam.
David

6

Der portabelste Weg, um den Hostnamen des aktuellen Computers in Java abzurufen, ist wie folgt:

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);
    }
}

8
Abgesehen von der Portabilität, wie ist diese Methode mit der fraglichen Methode zu vergleichen? Was sind Vor- / Nachteile?
Marquez

4

Wenn Sie nicht gegen die Verwendung einer externen Abhängigkeit von maven central sind, habe ich gethostname4j geschrieben, um dieses Problem für mich selbst zu lösen. Es verwendet nur JNA, um die Funktion gethostname von libc aufzurufen (oder den Computernamen unter Windows abzurufen) und gibt sie als Zeichenfolge an Sie zurück.

https://github.com/mattsheppard/gethostname4j


3
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();
            }
        }
    }
}

1
Was ist der Vorteil dieser Methode?
Sam Hasler

1
Dies ist ungültig und enthält nur den Namen der ersten Netzwerkkarte, die kein Loopback-Adapter ist.
Steve-o

Eigentlich ist es das erste Nicht-Loopback, das einen Hostnamen hat ... nicht unbedingt das erste Nicht-Loopback.
Adam Gent

1

Nur einzeilig ... plattformübergreifend (Windows-Linux-Unix-Mac (Unix)) [Funktioniert immer, kein DNS erforderlich]:

String hostname = new BufferedReader(
    new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
   .readLine();

Du bist fertig !!


InetAddress.getLocalHost (). GetHostName () funktioniert unter Unix nicht?
P Satish Patro

1
Das funktioniert, erfordert jedoch eine DNS-Auflösung. Wenn Sie von Ihrem Telefon aus mit einem WLAN-Tethering verbunden sind (um nur ein Beispiel zu nennen), verfügt es nicht über ein DNS, das den lokalen Host-Computer kennt, und es funktioniert nicht.
Dan Ortega

-2

InetAddress.getLocalHost (). GetHostName () ist der beste Ausweg, da dies die beste Abstraktion auf Entwicklerebene ist.


Ich suche nach einer Antwort, die sich mit einem Host befasst, den viele Hostnamen kennen.
Sam Hasler

@ SamHasler, wie ist Ihre spezifische Situation? Wie Arnout erklärte, gibt es verschiedene Möglichkeiten, je nachdem, was Sie benötigen.
Nick Knowlson
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.