Méthode recommandée pour obtenir le nom d'hôte en Java

275

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()

Mahendra
la source
De quelle pile technologique s'agit-il?
tom redfern
Je pense que le seul vrai nom soutenu par uname (uts_name) provient du RMI / JMX VMID, mais cela est spécifique à l'implémentation.
vérifie

Réponses:

336

À 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:

  • L'adresse IP peut ne pas être résolue en n'importe quel nom. Une mauvaise configuration DNS, une mauvaise configuration du système ou une mauvaise configuration du fournisseur peut en être la cause.
  • Un nom dans DNS peut avoir de nombreux alias appelés CNAME. Ceux-ci ne peuvent être résolus que dans une seule direction: nom à adresse. La direction inverse est ambiguë. Lequel est le nom "officiel"?
  • Un hôte peut avoir de nombreuses adresses IP différentes - et chaque adresse peut avoir de nombreux noms différents. Deux cas courants sont: un port Ethernet a plusieurs adresses IP "logiques" ou l'ordinateur a plusieurs ports Ethernet. Il est configurable qu'ils partagent une IP ou aient des IP différentes. C'est ce qu'on appelle "multi-home".
  • Un nom dans DNS peut se résoudre en plusieurs adresses IP. Et toutes ces adresses ne doivent pas se trouver sur le même ordinateur! (Cas d'utilisation: une forme simple d'équilibrage de charge)
  • Ne parlons même pas des adresses IP dynamiques.

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.

AH
la source
9
Pas tout à fait la réponse que j'espérais mais maintenant je sais comment poser une meilleure question, merci.
Sam Hasler
4
C'est une belle description des limites que tout logiciel (pas seulement un programme Java) a pour déterminer le nom de l'hôte dans le monde. Cependant, notez que getHostName est implémenté en termes de système d'exploitation sous-jacent, probablement de la même manière que le nom d'hôte / gethostname. Sur un système "normal", InetAddress.getLocalHost (). GetHostName () équivaut à appeler hostname / gethostname, vous ne pouvez donc pas vraiment dire que l'un échoue d'une manière différente de l'autre.
Peter Cardona
System.err.println (Runtime.getRuntime (). Exec ("hostname")); me donne ceci: java.lang.UNIXProcess@6eb2384f
user152468
5
L'implémentation de InetAddress.getLocalHost (). GetHostName () est en fait très déterministe :) Si vous suivez le code source, il appelle finalement gethostname () sous Windows et getaddrinfo () sur les systèmes Unixy. Le résultat est identique à l'utilisation de votre commande de nom d'hôte de système d'exploitation. Le nom d'hôte peut maintenant fournir une réponse que vous ne souhaitez pas utiliser, cela est possible pour de nombreuses raisons. En général, le logiciel doit obtenir le nom d'hôte de l'utilisateur dans un fichier de configuration, de cette façon, c'est toujours le nom d'hôte correct. Vous pouvez utiliser InetAddress.getLocalhost (). GetHostName () par défaut si l'utilisateur ne fournit pas de valeur.
Greg
93

InetAddress.getLocalHost().getHostName() est le moyen le plus portable.

exec("hostname")appelle en fait le système d'exploitation pour exécuter la hostnamecommande.

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.

Nick Knowlson
la source
12
L'exécution de getHostName () entraîne parfois une erreur. Par exemple, voici ce que j'obtiens dans une instance Linux Amazon EC2 AMI: java.net.UnknownHostException: nom ou service inconnu java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez
1
De plus, 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.
olenz
54

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

    1. Lisez la COMPUTERNAMEvariable d'environnement System.getenv().

    2. Exécutez hostname.exeet lisez la réponse

  • Sous Linux

    1. Lisez la HOSTNAMEvariable d'environnementSystem.getenv()

    2. Exécutez hostnameet lisez la réponse

    3. Lire /etc/hostname(pour ce faire, je suis en cattrain 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 $HOSTNAMEretourne 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 HOSTNAMEavant 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.

Malt
la source
3
Si vous export HOSTNAMEavant d'exécuter le code Java sur Ubuntu 14.04 LTS, il est également renvoyé par System.getenv("HOSTNAME").
legolas108
Vous devez explicitement export HOSTNAMEque Java l'utilise? Je pensais que ce devrait être un var env par défaut comme PATH.
David
2
Oui, c'est l'un des bogues Java qui ne sera jamais corrigé pour des raisons religieuses. Un autre doit être en mesure de définir le nom du processus. Bugs enregistrés il y a près de 20 ans.
Tuntable
Excellente réponse, très approfondie! Je ne sais pas pourquoi vous utilisez cet étrange délimiteur, surtout si l'on considère que chaque sortie imprimée contient une nouvelle ligne étrange. Quoi qu'il en soit, je mets à jour la réponse pour travailler avec MacOS, raccourcis l'indexOf pour contenir les appels et corrige le nom de la variable du système d'exploitation pour être plus cohérent avec les conventions de nom de variable standard.
Charlie
1
@Charlie le \Adélimiteur n'est qu'un hack pour lire l'intégralité du InputStream à l'aide de a Scanner.
Malt
24

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.

Arnout Engelen
la source
18

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.

Recherche du nom d'ordinateur

  • Sous Linux / Unix, le nom d'ordinateur est ce que vous obtenez de la fonction C gethostname(), ou de la hostnamecommande du shell ou de HOSTNAMEla 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 COMPUTERNAMEou de la GetComputerNamefonction 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.

Exemple Linux

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».

Exemple Solaris

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».

Quand allez-vous vraiment vous battre avec ça?

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.

Rapports de bogues / RFE

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.

peterh
la source
12

Les variables d'environnement peuvent également fournir un moyen utile - COMPUTERNAMEsous Windows, HOSTNAMEsur 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;
Thomas W
la source
6
StringUtils? Qu'est-ce que c'est?? (Je sais ce que vous voulez dire, je pense simplement que c'est un mauvais karma de faire appel à une bibliothèque externe à cette seule fin .. et en plus de ne pas le mentionner)
appel
23
C'est un mauvais karma d'écrire constamment des chèques «vides» manuellement. De très nombreux projets ont ces vérifications effectuées manuellement (comme vous le suggérez) et de manière incohérente, avec de nombreux bogues qui en résultent. Utilisez une bibliothèque.
Thomas W
15
Au cas où quelqu'un verrait cela et serait réellement confus StringUtils, son fourni par le projet Apache commons-lang. L'utiliser, ou quelque chose comme ça, est fortement recommandé.
JBCP
D'une manière ou d'une autre, System.getenv("HOSTNAME")est sorti nul sur Mac via Beanshell pour Java, mais a PATHété extrait correctement. Je pourrais aussi le faire echo $HOSTNAMEsur Mac, juste System.getenv ("HOSTNAME") semble avoir un problème. Étrange.
David
6

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);
    }
}
Desta Haileselassie Hagos
la source
8
La portabilité mise à part, comment cette méthode se compare-t-elle à la méthode de la question? Quels sont les avantages / inconvénients?
Marquez
4

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.

https://github.com/mattsheppard/gethostname4j

Matt Sheppard
la source
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();
            }
        }
    }
}
ThuVien247.net
la source
1
Quel est l'avantage de cette méthode?
Sam Hasler
1
Ceci n'est pas valide, il fournit uniquement le nom du premier NIC qui n'est pas un adaptateur de bouclage.
Steve-o
en fait, c'est le premier non-bouclage qui a un nom d'hôte ... pas nécessairement le premier non-bouclage.
Adam Gent
1

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é !!

Dan Ortega
la source
InetAddress.getLocalHost (). GetHostName () ne fonctionnera pas sous Unix?
P Satish Patro
1
Cela fonctionne mais nécessite une résolution DNS, que se passe-t-il si vous êtes connecté à un modem wifi depuis votre téléphone (pour ne citer qu'un exemple), il n'aura pas de DNS connaissant la machine hôte locale et cela ne fonctionnera pas.
Dan Ortega
-2

InetAddress.getLocalHost (). GetHostName () est le meilleur moyen de sortir des deux car c'est la meilleure abstraction au niveau du développeur.

java_mouse
la source
Je cherche une réponse qui traite d'un hôte connu par de nombreux noms d'hôtes.
Sam Hasler
@SamHasler, quelle est votre situation spécifique? Comme Arnout l'a expliqué, il existe différentes façons selon ce dont vous avez besoin.
Nick Knowlson