Laquelle des méthodes suivantes est la meilleure et la plus portable pour obtenir le nom d'hôte de l'ordinateur actuel en Java?
Runtime.getRuntime().exec("hostname")
contre
InetAddress.getLocalHost().getHostName()
Laquelle des méthodes suivantes est la meilleure et la plus portable pour obtenir le nom d'hôte de l'ordinateur actuel en Java?
Runtime.getRuntime().exec("hostname")
contre
InetAddress.getLocalHost().getHostName()
Réponses:
À strictement parler - vous n'avez pas d'autre choix que d'appeler l'un hostname(1)
ou l' autre - sur Unix gethostname(2)
. Il s'agit du nom de votre ordinateur. Toute tentative de déterminer le nom d'hôte par une adresse IP comme celle-ci
InetAddress.getLocalHost().getHostName()
est voué à l'échec dans certaines circonstances:
Ne confondez pas non plus le nom d'une adresse IP avec le nom de l'hôte (nom d'hôte). Une métaphore pourrait être plus claire:
Il y a une grande ville (serveur) appelée "Londres". À l'intérieur des murs de la ville, de nombreuses affaires se produisent. La ville possède plusieurs portes (adresses IP). Chaque porte a un nom ("North Gate", "River Gate", "Southampton Gate" ...) mais le nom de la porte n'est pas le nom de la ville. De plus, vous ne pouvez pas déduire le nom de la ville en utilisant le nom d'une porte - "North Gate" attraperait la moitié des plus grandes villes et pas seulement une ville. Cependant - un étranger (paquet IP) marche le long de la rivière et demande à un local: "J'ai une adresse étrange: 'Rivergate, deuxième à gauche, troisième maison'. Pouvez-vous m'aider?" Le local dit: "Bien sûr, vous êtes sur la bonne route, allez-y simplement et vous arriverez à destination dans une demi-heure."
Cela illustre à peu près je pense.
La bonne nouvelle est: le vrai nom d'hôte n'est généralement pas nécessaire. Dans la plupart des cas, tout nom qui se résout en une adresse IP sur cet hôte fera l'affaire. (L'étranger pourrait entrer dans la ville par Northgate, mais les habitants utiles traduisent la partie "2ème à gauche".)
Si les cas de coin restantes vous devez utiliser la définitive source de ce paramètre de configuration - qui est la fonction C gethostname(2)
. Cette fonction est également appelée par le programme hostname
.
InetAddress.getLocalHost().getHostName()
est le moyen le plus portable.
exec("hostname")
appelle en fait le système d'exploitation pour exécuter la hostname
commande.
Voici quelques autres réponses connexes sur SO:
EDIT: Vous devriez jeter un œil à la réponse d'AH ou à la réponse d' Arnout Engelen pour savoir pourquoi cela pourrait ne pas fonctionner comme prévu, selon votre situation. En tant que réponse pour cette personne qui a spécifiquement demandé le portable, je pense toujours que getHostName()
c'est bien, mais ils soulèvent de bons points qui devraient être pris en considération.
InetAddress.getLocalHost()
dans certains cas, renvoie le périphérique de bouclage. Dans ce cas, getHostName()
renvoie "localhost", ce qui n'est pas très utile.
Comme d'autres l'ont noté, l'obtention du nom d'hôte basé sur la résolution DNS n'est pas fiable.
Étant donné que cette question est malheureusement toujours d'actualité en 2018 , je voudrais partager avec vous ma solution indépendante du réseau, avec quelques tests sur différents systèmes.
Le code suivant tente d'effectuer les opérations suivantes:
Sous Windows
Lisez la COMPUTERNAME
variable d'environnement System.getenv()
.
Exécutez hostname.exe
et lisez la réponse
Sous Linux
Lisez la HOSTNAME
variable d'environnementSystem.getenv()
Exécutez hostname
et lisez la réponse
Lire /etc/hostname
(pour ce faire, je suis en cat
train d' exécuter, car l'extrait contient déjà du code à exécuter et à lire. Une simple lecture du fichier serait cependant préférable).
Le 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() : "";
}
}
Résultats pour différents systèmes d'exploitation:
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
Celui-ci est un peu étrange car echo $HOSTNAME
retourne le nom d'hôte correct, mais System.getenv("HOSTNAME")
ne fait pas:
Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"
EDIT: Selon legolas108 , System.getenv("HOSTNAME")
fonctionne sur Ubuntu 14.04 si vous exécutez export HOSTNAME
avant d'exécuter le code 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
"
Les noms des machines ont été remplacés mais j'ai conservé la capitalisation et la structure. Notez la nouvelle ligne supplémentaire lors de l'exécution hostname
, vous devrez peut-être en tenir compte dans certains cas.
export HOSTNAME
avant d'exécuter le code Java sur Ubuntu 14.04 LTS, il est également renvoyé par System.getenv("HOSTNAME")
.
export HOSTNAME
que Java l'utilise? Je pensais que ce devrait être un var env par défaut comme PATH.
\A
délimiteur n'est qu'un hack pour lire l'intégralité du InputStream à l'aide de a Scanner
.
InetAddress.getLocalHost().getHostName()
est mieux (comme expliqué par Nick), mais toujours pas très bon
Un hôte peut être connu sous de nombreux noms d'hôtes différents. Habituellement, vous rechercherez le nom d'hôte de votre hôte dans un contexte spécifique.
Par exemple, dans une application Web, vous recherchez peut-être le nom d'hôte utilisé par la personne qui a émis la demande que vous gérez actuellement. La meilleure façon de trouver celle-ci dépend du cadre que vous utilisez pour votre application Web.
Dans une sorte d'autre service accessible sur Internet, vous voudrez que le nom d'hôte via lequel votre service est disponible de «l'extérieur». En raison de procurations, de pare-feu, etc., il se peut que ce ne soit même pas un nom d'hôte sur la machine sur laquelle votre service est installé - vous pouvez essayer de trouver une valeur par défaut raisonnable, mais vous devriez certainement le rendre configurable pour celui qui l'installe.
Bien que ce sujet ait déjà été répondu, il y a plus à dire.
Tout d'abord: il est clair que nous avons besoin de quelques définitions ici. Le InetAddress.getLocalHost().getHostName()
vous donne le nom de l'hôte vu du point de vue du réseau . Les problèmes avec cette approche sont bien documentés dans les autres réponses: elle nécessite souvent une recherche DNS, elle est ambiguë si l'hôte a plusieurs interfaces réseau et elle échoue tout simplement parfois (voir ci-dessous).
Mais sur n'importe quel système d'exploitation, il existe également un autre nom. Nom de l'hôte qui est défini très tôt dans le processus de démarrage, bien avant l'initialisation du réseau. Windows y fait référence sous le nom de calcul , Linux l'appelle nom d'hôte du noyau et Solaris utilise le mot nodename . Je préfère le mot nom_ordinateur , je vais donc utiliser ce mot à partir de maintenant.
Sous Linux / Unix, le nom d'ordinateur est ce que vous obtenez de la fonction C gethostname()
, ou de la hostname
commande du shell ou de HOSTNAME
la variable d'environnement dans des shells de type Bash.
Sous Windows, le nom d'ordinateur est ce que vous obtenez de la variable d'environnement COMPUTERNAME
ou de la GetComputerName
fonction Win32 .
Java n'a aucun moyen d'obtenir ce que j'ai défini comme «nom d'ordinateur». Bien sûr, il existe des solutions de contournement comme décrit dans d'autres réponses, comme pour les appels Windows System.getenv("COMPUTERNAME")
, mais sur Unix / Linux, il n'y a pas de bonne solution de contournement sans recourir à JNI / JNA ou Runtime.exec()
. Si cela ne vous dérange pas une solution JNI / JNA, il y a gethostname4j qui est très simple et très facile à utiliser.
Passons à deux exemples, l'un de Linux et l'autre de Solaris, qui montrent comment vous pouvez facilement vous retrouver dans une situation où vous ne pouvez pas obtenir le nom d'ordinateur à l'aide de méthodes Java standard.
Sur un système nouvellement créé, où l'hôte lors de l'installation a été nommé «chicago», nous changeons maintenant le nom d'hôte du noyau:
$ hostnamectl --static set-hostname dallas
Maintenant, le nom d'hôte du noyau est 'dallas', comme en témoigne la commande hostname:
$ hostname
dallas
Mais nous avons encore
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
Il n'y a aucune mauvaise configuration là-dedans. Cela signifie simplement que le nom en réseau de l'hôte (ou plutôt le nom de l'interface de bouclage) est différent du nom d'ordinateur de l'hôte.
Maintenant, essayez de l'exécuter InetAddress.getLocalHost().getHostName()
et cela lancera java.net.UnknownHostException. Vous êtes fondamentalement coincé. Il n'y a aucun moyen de récupérer ni la valeur «dallas» ni la valeur «chicago».
L'exemple ci-dessous est basé sur Solaris 11.3.
L'hôte a été délibérément configuré pour que le nom de bouclage <> nodename.
En d'autres termes, nous avons:
$ svccfg -s system/identity:node listprop config
...
...
config/loopback astring chicago
config/nodename astring dallas
et le contenu de / etc / hosts:
:1 chicago localhost
127.0.0.1 chicago localhost loghost
et le résultat de la commande hostname serait:
$ hostname
dallas
Tout comme dans l'exemple Linux, un appel à InetAddress.getLocalHost().getHostName()
échouera avec
java.net.UnknownHostException: dallas: dallas: node name or service name not known
Tout comme l'exemple Linux, vous êtes maintenant bloqué. Il n'y a aucun moyen de récupérer ni la valeur «dallas» ni la valeur «chicago».
Très souvent, vous constaterez que InetAddress.getLocalHost().getHostName()
cela retournera en effet une valeur qui est égale au nom de l'ordinateur. Il n'y a donc aucun problème (à l'exception de la surcharge supplémentaire de la résolution de noms).
Le problème survient généralement dans les environnements PaaS où il y a une différence entre le nom de l'ordinateur et le nom de l'interface de bouclage. Par exemple, des personnes signalent des problèmes dans Amazon EC2.
Un peu de recherche révèle ce rapport de RFE: link1 , lien2 . Cependant, à en juger par les commentaires sur ce rapport, le problème semble avoir été largement mal compris par l'équipe du JDK, il est donc peu probable qu'il soit traité.
J'aime la comparaison dans le RFE avec d'autres langages de programmation.
Les variables d'environnement peuvent également fournir un moyen utile - COMPUTERNAME
sous Windows, HOSTNAME
sur la plupart des shells Unix / Linux modernes.
Voir: https://stackoverflow.com/a/17956000/768795
J'utilise ces méthodes comme des méthodes «supplémentaires» InetAddress.getLocalHost().getHostName()
, car comme plusieurs personnes le soulignent, cette fonction ne fonctionne pas dans tous les environnements.
Runtime.getRuntime().exec("hostname")
est un autre supplément possible. À ce stade, je ne l'ai pas utilisé.
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
, son fourni par le projet Apache commons-lang. L'utiliser, ou quelque chose comme ça, est fortement recommandé.
System.getenv("HOSTNAME")
est sorti nul sur Mac via Beanshell pour Java, mais a PATH
été extrait correctement. Je pourrais aussi le faire echo $HOSTNAME
sur Mac, juste System.getenv ("HOSTNAME") semble avoir un problème. Étrange.
La façon la plus portable d'obtenir le nom d'hôte de l'ordinateur actuel en Java est la suivante:
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);
}
}
Si vous n'êtes pas contre l'utilisation d'une dépendance externe de maven central, j'ai écrit gethostname4j pour résoudre ce problème par moi-même. Il utilise simplement JNA pour appeler la fonction gethostname de libc (ou obtient le ComputerName sous Windows) et vous le renvoie sous forme de chaîne.
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();
}
}
}
}
Juste une ligne ... multiplateforme (Windows-Linux-Unix-Mac (Unix)) [Fonctionne toujours, aucun DNS requis]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
Vous avez terminé !!
InetAddress.getLocalHost (). GetHostName () est le meilleur moyen de sortir des deux car c'est la meilleure abstraction au niveau du développeur.