Quale dei seguenti è il modo migliore e più portatile per ottenere il nome host del computer corrente in Java?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Quale dei seguenti è il modo migliore e più portatile per ottenere il nome host del computer corrente in Java?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Risposte:
A rigor di termini - non hai altra scelta che chiamare uno hostname(1)
o - su Unix gethostname(2)
. Questo è il nome del tuo computer. Qualsiasi tentativo di determinare il nome host tramite un indirizzo IP come questo
InetAddress.getLocalHost().getHostName()
è destinato a fallire in alcune circostanze:
Inoltre, non confondere il nome di un indirizzo IP con il nome dell'host (nome host). Una metafora potrebbe rendere più chiaro:
C'è una grande città (server) chiamata "Londra". All'interno delle mura della città accadono molti affari. La città ha diverse porte (indirizzi IP). Ogni porta ha un nome ("North Gate", "River Gate", "Southampton Gate" ...) ma il nome della porta non è il nome della città. Inoltre, non puoi dedurre il nome della città usando il nome di una porta: "North Gate" catturerebbe metà delle città più grandi e non solo una città. Tuttavia - uno sconosciuto (pacchetto IP) cammina lungo il fiume e chiede a un locale: "Ho uno strano indirizzo: 'Rivergate, seconda a sinistra, terza casa'. Mi potete aiutare?" Il locale dice: "Certo, sei sulla strada giusta, semplicemente vai avanti e arriverai a destinazione entro mezz'ora".
Questo lo illustra praticamente penso.
La buona notizia è: il vero nome host di solito non è necessario. Nella maggior parte dei casi, funzionerà qualsiasi nome che si risolve in un indirizzo IP su questo host. (Lo sconosciuto potrebbe entrare nella città da Northgate, ma i locali utili tradurranno la parte "2a sinistra".)
Se i rimanenti casi d'angolo è necessario utilizzare la definitiva fonte di questa impostazione di configurazione - che è la funzione C gethostname(2)
. Quella funzione è anche chiamata dal programma hostname
.
InetAddress.getLocalHost().getHostName()
è il modo più portatile.
exec("hostname")
chiama effettivamente il sistema operativo per eseguire il hostname
comando.
Ecco un paio di altre risposte correlate su SO:
EDIT: dovresti dare un'occhiata alla risposta di AH o alla risposta di Arnout Engelen per i dettagli sul perché questo potrebbe non funzionare come previsto, a seconda della tua situazione. Come risposta per questa persona che ha specificamente richiesto il portatile, penso ancora getHostName()
che vada bene, ma fanno emergere alcuni punti positivi che dovrebbero essere considerati.
InetAddress.getLocalHost()
in alcuni casi restituisce il dispositivo di loopback. In tal caso, getHostName()
restituisce "localhost", che non è molto utile.
Come altri hanno notato, ottenere il nome host basato sulla risoluzione DNS non è affidabile.
Dal momento che questa domanda è purtroppo ancora rilevante nel 2018 , vorrei condividere con voi la mia soluzione indipendente dalla rete, con alcuni test eseguiti su sistemi diversi.
Il codice seguente tenta di effettuare le seguenti operazioni:
Su Windows
Leggi la COMPUTERNAME
variabile d'ambiente attraverso System.getenv()
.
Esegui hostname.exe
e leggi la risposta
Su Linux
Leggi la HOSTNAME
variabile d'ambiente attraversoSystem.getenv()
Esegui hostname
e leggi la risposta
Leggi /etc/hostname
(per fare ciò sto eseguendo cat
poiché lo snippet contiene già codice da eseguire e leggere. Tuttavia, leggere semplicemente il file sarebbe meglio).
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 è un po 'strano poiché echo $HOSTNAME
restituisce il nome host corretto, ma System.getenv("HOSTNAME")
non:
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 capitalizzazione e la struttura. Nota la nuova riga aggiuntiva durante l'esecuzione hostname
, in alcuni casi potrebbe essere necessario tenerne conto.
export HOSTNAME
prima di eseguire il codice Java su Ubuntu 14.04 LTS viene anche restituito da System.getenv("HOSTNAME")
.
export HOSTNAME
che Java lo utilizzi? Ho pensato che dovrebbe essere un ambiente predefinito come PATH.
\A
delimitatore è solo un trucco per leggere l'intero InputStream usando a Scanner
.
InetAddress.getLocalHost().getHostName()
è meglio (come spiegato da Nick), ma ancora non molto buono
Un host può essere conosciuto con molti nomi host diversi. Di solito cercherai il nome host che il tuo host ha in un contesto specifico.
Ad esempio, in un'applicazione Web, potresti cercare il nome host utilizzato da chiunque abbia emesso la richiesta che stai gestendo. Il modo migliore per individuarlo dipende dal framework utilizzato per l'applicazione Web.
In una sorta di altro servizio con connessione a Internet, vorrai il nome host attraverso il quale il tuo servizio è disponibile dall'esterno. A causa di proxy, firewall, ecc., Questo potrebbe non essere nemmeno un nome host sulla macchina su cui è installato il tuo servizio - potresti provare a trovare un valore predefinito ragionevole, ma dovresti sicuramente renderlo configurabile per chiunque lo installi.
Sebbene a questo argomento sia già stata data risposta, c'è altro da dire.
Prima di tutto: chiaramente abbiamo bisogno di alcune definizioni qui. La InetAddress.getLocalHost().getHostName()
si dà il nome dell'host come si è visto dal punto di vista della 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 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 nomecomputer , Linux lo chiama kernel hostname e Solaris usa la parola nodename . Mi piace di più la parola nomecomputer , quindi userò quella parola d'ora in poi.
Su Linux / Unix il nomecomputer è quello che si ottiene dalla funzione C gethostname()
, o hostname
comando da shell o HOSTNAME
variabile d'ambiente in shell tipo Bash.
Su Windows il nomecomputer è quello che ottieni dalla variabile d'ambiente COMPUTERNAME
o dalla GetComputerName
funzione Win32 .
Java non ha modo di ottenere quello che ho definito "nomecomputer". Certo, ci sono soluzioni alternative come descritto in altre risposte, come per le chiamate Windows System.getenv("COMPUTERNAME")
, ma su Unix / Linux non c'è soluzione alternativa senza ricorrere a JNI / JNA o Runtime.exec()
. Se non ti dispiace una soluzione JNI / JNA, c'è gethostname4j che è morto semplice e molto facile da usare.
Andiamo avanti con due esempi, uno da Linux e uno da Solaris, che dimostrano come si può facilmente entrare in una situazione in cui non è possibile ottenere il nomecomputer usando i metodi Java standard.
Su un sistema appena creato, in cui l'host durante l'installazione è stato chiamato "chicago", ora cambiamo il cosiddetto nome host del kernel:
$ hostnamectl --static set-hostname dallas
Ora il nome host del kernel è 'dallas', come risulta dal comando hostname:
$ hostname
dallas
Ma abbiamo ancora
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
Non c'è configurazione errata in questo. Significa solo che il nome di rete dell'host (o piuttosto il nome dell'interfaccia di loopback) è diverso dal nome computer dell'host.
Ora prova a eseguire InetAddress.getLocalHost().getHostName()
e verrà lanciato java.net.UnknownHostException. Sei praticamente bloccato. Non c'è modo di recuperare né il valore "dallas" né il valore "chicago".
L'esempio seguente si basa su Solaris 11.3.
L'host è stato deliberatamente configurato in modo che il nome di 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 con cui una chiamata InetAddress.getLocalHost().getHostName()
fallirà
java.net.UnknownHostException: dallas: dallas: node name or service name not known
Proprio come nell'esempio di Linux, ora sei bloccato. Non c'è modo di recuperare né il valore "dallas" né il valore "chicago".
Molto spesso scoprirai che InetAddress.getLocalHost().getHostName()
in effetti restituirà un valore uguale al nomecomputer. Quindi non ci sono problemi (tranne per l'overhead aggiunto della risoluzione dei nomi).
Il problema sorge in genere negli ambienti PaaS in cui esiste una differenza tra nomecomputer e il nome dell'interfaccia di loopback. Ad esempio, le persone segnalano problemi in Amazon EC2.
Un po 'di ricerca rivela questo rapporto RFE: link1 , link2 . Tuttavia, a giudicare dai commenti su tale rapporto, il problema sembra essere stato ampiamente frainteso dal team JDK, quindi è improbabile che venga affrontato.
Mi piace il confronto tra RFE e altri linguaggi di programmazione.
Le variabili di 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()
, dal momento che come molte persone sottolineano, quella funzione non funziona in tutti gli ambienti.
Runtime.getRuntime().exec("hostname")
è un altro possibile supplemento. In questa fase, 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;
StringUtils
, è fornito dal progetto commons-lang di Apache. Si consiglia vivamente di usarlo o qualcosa di simile.
System.getenv("HOSTNAME")
è risultato nullo su Mac tramite Beanshell per Java, ma è PATH
stato estratto ok. Potrei farlo anche echo $HOSTNAME
su Mac, solo System.getenv ("HOSTNAME") sembra avere problemi. Strano.
Il modo più portatile 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);
}
}
Se non sei contrario all'utilizzo di una dipendenza esterna da 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 te lo restituisce come una stringa.
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();
}
}
}
}
Just one-liner ... cross platform (Windows-Linux-Unix-Mac (Unix)) [Funziona sempre, non è richiesto il DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
Hai finito !!
InetAddress.getLocalHost (). GetHostName () è il modo migliore per uscire dai due in quanto questa è la migliore astrazione a livello di sviluppatore.