it-swarm.it

Modo consigliato per ottenere l'hostname in Java

Quale dei seguenti è il modo migliore e più portabile per ottenere il nome host del computer corrente in Java?

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

vs

InetAddress.getLocalHost().getHostName()

242
Mahendra

A rigor di termini - non hai altra scelta che chiamare hostname(1) o - su Unix gethostname(2). Questo è il nome del tuo computer. Qualsiasi tentativo di determinare il nome host da un indirizzo IP come questo

InetAddress.getLocalHost().getHostName()

è destinato a fallire in alcune circostanze:

  • L'indirizzo IP potrebbe non risolversi in alcun nome. La configurazione errata del DNS, l'errata configurazione del sistema o l'impostazione errata del provider potrebbero essere la causa di ciò.
  • Un nome in DNS può avere molti alias chiamati CNAME. Questi possono essere risolti solo in una direzione in modo appropriato: nome per indirizzo. La direzione inversa è ambigua. Qual è il nome "ufficiale"?
  • Un host può avere molti diversi indirizzi IP e ogni indirizzo può avere molti nomi diversi. Due casi comuni sono: una porta ethernet ha diversi indirizzi IP "logici" o il computer ha diverse porte ethernet. È configurabile se condividono un IP o hanno IP diversi. Questo è chiamato "multihomed".
  • Un nome nel DNS può essere risolto in diversi indirizzi IP. E non tutti questi indirizzi devono trovarsi sullo stesso computer! (Usecase: una forma semplice di bilanciamento del carico)
  • Non iniziamo nemmeno a parlare di indirizzi IP dinamici.

Inoltre, non confondere il nome di un indirizzo IP con il nome dell'host (nome host). Una metafora potrebbe renderlo più chiaro:

C'è una grande città (server) chiamata "Londra". All'interno delle mura cittadine accadono molti affari. La città ha diverse porte (indirizzi IP). Ogni porta ha un nome ("North Gate", "River Gate", "Southampton Gate" ...) ma il nome del cancello non è il nome della città. Inoltre, non è possibile dedurre il nome della città utilizzando il nome di un cancello: "North Gate" catturerebbe metà delle città più grandi e non solo una città. Tuttavia - un estraneo (pacchetto IP) cammina lungo il fiume e chiede a un locale: "Ho uno strano indirizzo:" Rivergate, seconda a sinistra, terza casa. "Potete aiutarmi?" Il locale dice: "Certo, sei sulla strada giusta, semplicemente vai avanti e arriverai a destinazione entro mezz'ora".

Questo lo dimostra più o meno credo.

La buona notizia è: Il real hostname è di solito non necessario. Nella maggior parte dei casi, qualsiasi nome che si risolve in un indirizzo IP su questo host lo farà. (Lo straniero potrebbe entrare in città da Northgate, ma i locali utili traducono la parte "2a sinistra".)

Se i casi di angolo restanti devi usare il definitivo sorgente di questa impostazione di configurazione - che è la funzione C gethostname(2). Quella funzione è anche chiamata dal programma hostname.

304
A.H.

InetAddress.getLocalHost().getHostName() è il modo più portabile.

exec("hostname") chiama effettivamente al sistema operativo per eseguire il comando hostname.

Ecco alcune altre risposte correlate su SO:

EDIT: Dovresti dare un'occhiata a Risposta di AH o La risposta di Arnout Engelen per i dettagli sul motivo per cui questo potrebbe non funzionare come previsto, a seconda della situazione. Come risposta per questa persona che ha specificamente richiesto il portatile, continuo a pensare che getHostName() funzioni bene, ma presentano alcuni aspetti positivi che dovrebbero essere considerati.

89
Nick Knowlson

Come altri hanno notato, ottenere il nome host basato sulla risoluzione DNS è inaffidabile.

Poiché questa domanda è purtroppo ancora pertinente in 2018 , vorrei condividere con voi la mia soluzione indipendente dalla rete, con alcune esecuzioni di test su sistemi diversi.

Il seguente codice prova a fare quanto segue:

  • Su Windows

    1. Leggi la variabile d'ambiente COMPUTERNAME tramite System.getenv().

    2. Esegui hostname.exe e leggi la risposta

  • Su Linux

    1. Leggi la variabile d'ambiente HOSTNAME tramite System.getenv()

    2. Esegui hostname e leggi la risposta

    3. Leggi /etc/hostname (per fare questo sto eseguendo cat dato che lo snippet contiene già del codice da eseguire e leggere. Tuttavia, leggere il file sarebbe meglio, comunque).

Il codice:

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

Risultati per diversi sistemi operativi:

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 Questo è piuttosto strano dato che echo $HOSTNAME restituisce il nome host corretto, ma System.getenv("HOSTNAME") no:

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

EDIT: Secondo legolas108 , System.getenv("HOSTNAME") funziona su Ubuntu 14.04 se si esegue export HOSTNAME prima di eseguire il codice 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
"

I nomi delle macchine sono stati sostituiti, ma ho mantenuto la maiuscola e la struttura. Nota la nuova riga extra quando si esegue hostname, potrebbe essere necessario tenerne conto in alcuni casi.

47
Malt

InetAddress.getLocalHost().getHostName() è migliore (come spiegato da Nick), ma non è ancora molto buono

Un host può essere conosciuto con molti nomi host diversi. Di solito cercherete il nome host che l'host ha in un contesto specifico.

Ad esempio, in un'applicazione Web, è possibile che si stia cercando il nome host utilizzato da chiunque abbia emesso la richiesta attualmente gestita. Come meglio trovare quello dipende dal framework che stai usando per la tua applicazione web.

In una sorta di altro servizio rivolto a Internet, vorrete che il nome host del vostro servizio sia disponibile dall'esterno. A causa di proxy, firewall, ecc., Questo potrebbe non essere nemmeno un nome host sulla macchina su cui è installato il servizio - potresti provare a trovare un valore predefinito ragionevole, ma dovresti sicuramente renderlo configurabile per chiunque lo installi.

23
Arnout Engelen

Sebbene questo argomento abbia già avuto risposta, c'è ancora altro da dire.

Prima di tutto: Chiaramente abbiamo bisogno di alcune definizioni qui. InetAddress.getLocalHost().getHostName() fornisce il nome dell'host come visto da una prospettiva di rete . I problemi con questo approccio sono ben documentati nelle altre risposte: spesso richiede una ricerca DNS, è ambiguo se l'host ha più interfacce di rete e talvolta semplicemente fallisce (vedi sotto).

Ma su qualsiasi sistema operativo c'è anche un altro nome. Un nome dell'host che viene definito molto presto nel processo di avvio, molto prima dell'inizializzazione della rete. Windows si riferisce a questo come computername , Linux lo chiama kernel hostname e Solaris utilizza Word nodename . Mi piace il meglio il Word computername , quindi userò quella parola d'ora in poi.

Trovare il nomecomputer

  • Su Linux/Unix il nomecomputer è ciò che si ottiene dalla funzione C gethostname(), o dal comando hostname dalla shell o dalla variabile d'ambiente HOSTNAME in shell di tipo Bash.

  • Su Windows il nomecomputer è quello che ottieni dalla variabile di ambiente COMPUTERNAME o dalla funzione GetComputerName di Win32.

Java non ha modo di ottenere quello che ho definito come 'nomecomputer'. Certo, ci sono soluzioni alternative come descritto in altre risposte, come per Windows che chiama System.getenv("COMPUTERNAME"), ma su Unix/Linux non c'è buona soluzione alternativa senza ricorrere a JNI/JNA o Runtime.exec(). Se non ti dispiace una soluzione JNI/JNA allora c'è gethostname4j che è semplice e molto facile da usare.

Andiamo avanti con due esempi, uno da Linux e uno da Solaris, che dimostrano come si possa facilmente entrare in una situazione in cui non è possibile ottenere il nomecomputer usando i metodi standard di Java.

Esempio di Linux

In un sistema appena creato, in cui l'host durante l'installazione è stato denominato 'chicago', ora cambiamo il cosiddetto hostname del kernel:

$ hostnamectl --static set-hostname dallas

Ora il nome host del kernel è "dallas", come risulta evidente dal comando hostname:

$ hostname
dallas

Ma abbiamo ancora

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Non c'è una configurazione errata in questo. Significa solo che il nome di rete dell'Host (o piuttosto il nome dell'interfaccia di loopback) è diverso dal nomecomputer dell'Host.

Ora prova a eseguire InetAddress.getLocalHost().getHostName() e genererà Java.net.UnknownHostException. Sei praticamente bloccato. Non c'è modo di recuperare né il valore "dallas" né il valore "chicago".

Esempio di Solaris

L'esempio seguente è basato su Solaris 11.3.

L'host è stato deliberatamente configurato in modo che il nome loopback <> nodename.

In altre parole abbiamo:

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

e il contenuto di/etc/hosts:

:1 chicago localhost
127.0.0.1 chicago localhost loghost

e il risultato del comando hostname sarebbe:

$ hostname
dallas

Proprio come nell'esempio Linux, una chiamata a InetAddress.getLocalHost().getHostName() fallirà con

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

Proprio come l'esempio di Linux, ora sei bloccato. Non c'è modo di recuperare né il valore "dallas" né il valore "chicago".

Quando lotterai davvero con questo?

Molto spesso scoprirete che InetAddress.getLocalHost().getHostName() restituirà effettivamente un valore che è uguale al nomecomputer. Quindi non ci sono problemi (eccetto il sovraccarico aggiuntivo della risoluzione dei nomi).

Il problema si presenta in genere negli ambienti PaaS in cui esiste una differenza tra computername e il nome dell'interfaccia di loopback. Ad esempio, le persone segnalano problemi in Amazon EC2.

Segnalazioni Bug/RFE

Un po 'di ricerca rivela questo rapporto RFE: link1 , link2 . Tuttavia, a giudicare dai commenti su quel rapporto, il problema sembra essere stato in gran parte frainteso dal team JDK, quindi è improbabile che verrà affrontato.

Mi piace il confronto in RFE con altri linguaggi di programmazione.

10
peterh

Le variabili d'ambiente possono anche fornire un mezzo utile - COMPUTERNAME su Windows, HOSTNAME sulla maggior parte delle moderne shell Unix/Linux.

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

Sto usando questi come metodi "supplementari" per InetAddress.getLocalHost().getHostName(), poiché, come molti sottolineano, quella funzione non funziona in tutti gli ambienti.

Runtime.getRuntime().exec("hostname") è un altro possibile supplemento. A questo punto, non l'ho usato.

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;
10
Thomas W

Il modo più portabile per ottenere il nome host del computer corrente in Java è il seguente:

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

Se non si è contrari all'uso di una dipendenza esterna da parte di maven central, ho scritto gethostname4j per risolvere questo problema da solo. Usa semplicemente JNA per chiamare la funzione gethostname di libc (o ottiene ComputerName su Windows) e lo restituisce come stringa.

https://github.com/mattsheppard/gethostname4j

2
Matt Sheppard