360 Stimmen

Empfohlener Weg zur Ermittlung des Hostnamens in Java

Welche der folgenden Möglichkeiten ist die beste und portabelste, um den Hostnamen des aktuellen Computers in Java zu ermitteln?

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

gegen

InetAddress.getLocalHost().getHostName()

429voto

A.H. Punkte 60629

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

InetAddress.getLocalHost().getHostName()

ist unter bestimmten Umständen zum Scheitern verurteilt:

  • Die IP-Adresse kann möglicherweise nicht in einen Namen aufgelöst werden. Der Grund dafür kann eine fehlerhafte DNS-Einrichtung, eine fehlerhafte Systemeinrichtung oder eine fehlerhafte Provider-Einrichtung sein.
  • Ein Name im DNS kann viele Aliasnamen haben, die CNAMEs genannt werden. Diese können nur in eine Richtung aufgelöst werden: Name nach Adresse. Die umgekehrte Richtung ist zweideutig. Welches 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-Anschluss hat mehrere "logische" IP-Adressen oder der Computer hat mehrere Ethernet-Anschlüsse. Es ist konfigurierbar, ob sie sich eine IP teilen oder unterschiedliche IPs haben. Dies wird als "multihomed" bezeichnet.
  • Ein Name im DNS kann zu mehreren IP-Adressen aufgelöst werden. Und nicht alle diese Adressen müssen sich auf demselben Computer befinden! (Anwendungsfall: Eine einfache Form des Lastausgleichs)
  • Von dynamischen IP-Adressen wollen wir gar nicht erst reden.

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

Es gibt eine große Stadt (Server) namens "London". Innerhalb der Stadtmauern finden viele Geschäfte statt. 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. Auch kann man aus dem Namen eines Gates nicht auf den Namen der Stadt schließen - "North Gate" würde die Hälfte der größeren Städte erfassen und nicht nur eine Stadt. Wie auch immer - ein Fremder (IP-Paket) geht am Fluss entlang und fragt einen Einheimischen: "Ich habe eine seltsame Adresse: Rivergate, zweites Haus links, drittes Haus". Können Sie mir helfen?" Der Einheimische sagt: "Natürlich, Sie sind auf der richtigen Straße, gehen Sie einfach weiter und Sie werden in einer halben Stunde an Ihrem Ziel sein."

Dies veranschaulicht es ziemlich gut, denke ich.

Die gute Nachricht ist: Die real Hostname ist in der Regel nicht erforderlich. In den meisten Fällen genügt jeder Name, der sich in eine IP-Adresse auf diesem Rechner auflösen lässt. (Der Fremde könnte die Stadt über Northgate betreten, aber hilfsbereite Einheimische übersetzen den Teil "2nd left").

In den übrigen Eckfällen müssen Sie die endgültig Quelle für diese Konfigurationseinstellung - die C-Funktion gethostname(2) . Diese Funktion wird auch von dem Programm hostname .

112voto

Nick Knowlson Punkte 6965

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

exec("hostname") das Betriebssystem auffordert, die hostname Befehl.

Hier sind ein paar andere Antworten auf SO:

EDITAR: Werfen Sie einen Blick auf A.H.s Antwort o Die Antwort von Arnout Engelen für Einzelheiten darüber, warum dies je nach Ihrer Situation nicht wie erwartet funktioniert. Als Antwort für die Person, die speziell nach tragbaren Geräten gefragt hat, denke ich immer noch getHostName() ist in Ordnung, aber sie bringen einige gute Punkte vor, die berücksichtigt werden sollten.

80voto

Malt Punkte 27449

Wie bereits von anderen erwähnt, ist die Ermittlung des Hostnamens auf der Grundlage der DNS-Auflösung unzuverlässig.

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

Der folgende Code versucht, Folgendes zu tun:

  • Unter Windows

    1. Lesen Sie die COMPUTERNAME Umgebungsvariable durch System.getenv() .

    2. Ausführen hostname.exe und lesen Sie die Antwort

  • Unter Linux

    1. Lesen Sie die HOSTNAME Umgebungsvariable durch System.getenv()

    2. Ausführen hostname und lesen Sie die Antwort

    3. Lesen Sie /etc/hostname (zu diesem Zweck führe ich Folgendes aus cat da das Snippet bereits Code zum Ausführen und Lesen enthält. Das einfache Lesen der Datei wäre jedoch besser).

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 Fall ist etwas seltsam, da echo $HOSTNAME gibt den richtigen Hostnamen zurück, 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
"

EDITAR: Nach Angaben von legolas108 , System.getenv("HOSTNAME") funktioniert unter Ubuntu 14.04, wenn Sie export HOSTNAME bevor der 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ßschreibung und die Struktur beibehalten. Beachten Sie den zusätzlichen Zeilenumbruch bei der Ausführung von hostname müssen Sie dies in einigen Fällen berücksichtigen.

28voto

Arnout Engelen Punkte 6250

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 Ihres Rechners in einem bestimmten Zusammenhang.

In einer Webanwendung könnten Sie zum Beispiel nach dem Hostnamen suchen, der von demjenigen verwendet wird, der die Anfrage gestellt hat, die Sie gerade bearbeiten. Wie Sie diesen am besten finden, hängt davon ab, welches Framework Sie für Ihre Webanwendung verwenden.

Bei anderen Diensten, die mit dem Internet verbunden sind, benötigen Sie den Hostnamen, über den Ihr Dienst von "außen" erreichbar ist. Aufgrund von Proxies, Firewalls usw. ist dies möglicherweise nicht einmal ein Hostname auf dem Rechner, auf dem Ihr Dienst installiert ist - Sie können versuchen, einen vernünftigen Standard zu finden, aber Sie sollten dies auf jeden Fall für denjenigen, der den Dienst installiert, konfigurierbar machen.

26voto

peterh Punkte 16923

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

Zuallererst: Wir brauchen hier ganz klar einige Definitionen. Die InetAddress.getLocalHost().getHostName() gibt Ihnen den Namen des Hosts aus der Netzperspektive betrachtet . Die Probleme mit diesem Ansatz sind in den anderen Antworten gut dokumentiert: Er erfordert oft eine DNS-Abfrage, ist mehrdeutig, wenn der Host mehrere Netzwerkschnittstellen hat, und schlägt manchmal einfach fehl (siehe unten).

Aber auf jedem Betriebssystem gibt es auch einen anderen Namen. Ein Name des Hosts, der sehr früh im Boot-Prozess definiert wird, lange bevor das Netzwerk initialisiert wird. Windows bezeichnet diesen Namen als Computername unter Linux heißt es Kernel-Hostname und Solaris verwendet das Wort Nodename . Am besten gefällt mir das Wort Computername Deshalb werde ich dieses Wort von nun an verwenden.

Suche nach dem Computernamen

  • Unter Linux/Unix ist der Computername das, was Sie von der C-Funktion erhalten gethostname() ou hostname Befehl aus der Shell oder HOSTNAME Umgebungsvariable in Bash-ähnlichen Shells.

  • Unter Windows ist der Computername das, was Sie aus der Umgebungsvariablen COMPUTERNAME oder Win32 GetComputerName Funktion.

Java hat keine Möglichkeit, das zu erhalten, was ich als 'Computername' definiert habe. Sicher, es gibt Umgehungsmöglichkeiten, wie in anderen Antworten beschrieben, z. B. für den Aufruf von Windows System.getenv("COMPUTERNAME") aber unter Unix/Linux gibt es keinen guten Workaround, ohne auf JNI/JNA oder Runtime.exec() . Wenn Sie nichts gegen eine JNI/JNA-Lösung haben, gibt es gethostname4j die ganz einfach und sehr leicht zu bedienen ist.

Lassen Sie uns mit zwei Beispielen fortfahren, eines unter Linux und eines unter Solaris, die zeigen, wie Sie leicht in eine Situation geraten können, in der Sie den Computernamen nicht mit Standard-Java-Methoden ermitteln können.

Beispiel Linux

Auf einem neu erstellten System, bei dem der Host während der Installation als 'chicago' bezeichnet wurde, ändern wir nun den sogenannten Kernel-Hostnamen:

$ hostnamectl --static set-hostname dallas

Der Kernel-Hostname lautet nun 'dallas', wie aus dem Befehl hostname hervorgeht:

$ hostname
dallas

Aber wir haben noch

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Das ist keine Fehlkonfiguration. Es bedeutet lediglich, dass der Netzwerkname des Hosts (oder vielmehr der Name der Loopback-Schnittstelle) nicht mit dem Computernamen des Hosts übereinstimmt.

Versuchen Sie nun die Ausführung von InetAddress.getLocalHost().getHostName() und es wird java.net.UnknownHostException ausgelöst. Sie sitzen 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 <> Nodename ist.

Mit anderen Worten: Wir haben:

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

und der 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 wird ein Aufruf von InetAddress.getLocalHost().getHostName() wird scheitern mit

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

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

Wann werden Sie sich damit wirklich auseinandersetzen?

Sehr oft werden Sie feststellen, dass InetAddress.getLocalHost().getHostName() wird tatsächlich einen Wert zurückgeben, der dem Computernamen entspricht. Es gibt also kein Problem (abgesehen von dem zusätzlichen Overhead der Namensauflösung).

Das Problem tritt typischerweise in PaaS-Umgebungen auf, wo es einen Unterschied zwischen Computername und dem Namen der Loopback-Schnittstelle gibt. Zum Beispiel berichten Leute über Probleme in Amazon EC2.

Fehler/RFE-Berichte

Eine kurze Suche ergibt 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.

Mir gefällt der Vergleich im RFE mit anderen Programmiersprachen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X