web-dev-qa-db-fra.com

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

Quel est le moyen le plus portable et le plus simple d’obtenir le nom d’hôte de l’ordinateur actuel en Java?

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

contre

InetAddress.getLocalHost().getHostName()

254
Mahendra

À proprement parler, vous n'avez pas d'autre choix que d'appeler soit hostname(1), soit - sous Unix gethostname(2). Ceci est le 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 sous un nom. Une mauvaise configuration DNS, système ou fournisseur peut en être la cause.
  • Un nom dans DNS peut avoir plusieurs alias appelés CNAME. Celles-ci ne peuvent être résolues correctement que dans un sens: nom à adresse. Le sens inverse est ambigu. Lequel est le nom "officiel"?
  • Un hôte peut avoir plusieurs adresses IP différentes - et chaque adresse peut avoir plusieurs noms différents. Deux cas courants sont les suivants: un port Ethernet a plusieurs adresses IP "logiques" ou l'ordinateur a plusieurs ports Ethernet. Il est configurable qu’ils partagent une adresse IP ou aient des adresses IP différentes. C'est ce qu'on appelle "multi-hôtes".
  • Un nom dans DNS peut être résolu en plusieurs adresses IP. Et toutes ces adresses ne doivent pas nécessairement se trouver sur le même ordinateur! (Usecase: une forme simple d'équilibrage de charge)
  • Ne commençons même pas à parler d'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 le rendre plus clair:

Il y a une grande ville (serveur) appelée "Londres". À l'intérieur des murs de la ville, beaucoup de choses se passent. La ville a 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. Aussi, vous ne pouvez pas déduire le nom de la ville en utilisant le nom d'une porte - "North Gate" prendrait 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?" La section locale a déclaré: "Bien sûr, vous êtes sur la bonne route, continuez et vous arriverez à destination dans une demi-heure."

Cela illustre bien ce que je pense.

La bonne nouvelle est que: le nom d'hôte réel 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 peut entrer dans la ville par Northgate, mais les habitants, très serviables, traduisent la partie "2e à gauche".)

Si les coins restants correspondent, vous devez utiliser la source définitive de ce paramètre de configuration - qui est la fonction C gethostname(2). Cette fonction est également appelée par le programme hostname.

317
A.H.

InetAddress.getLocalHost().getHostName() est la méthode la plus portable.

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

Voici quelques autres réponses sur SO:

EDIT: Vous devriez jeter un oeil à réponse d'AH ou réponse d'Arnout Engelen pour plus de détails pourquoi cela pourrait ne pas fonctionner comme prévu, selon votre situation. Pour répondre à cette personne qui a spécifiquement demandé le portable, je pense toujours que getHostName() va bien, mais ils soulèvent quelques points intéressants à prendre en compte.

90
Nick Knowlson

Comme d'autres l'ont fait remarquer, l'obtention du nom d'hôte en fonction de la résolution DNS n'est pas fiable.

Comme cette question est malheureusement toujours d'actualité pour 2018 , j'aimerais partager avec vous ma solution indépendante du réseau, avec quelques essais sur différents systèmes.

Le code suivant tente de faire ce qui suit:

  • Sur Windows

    1. Lisez la variable d'environnement COMPUTERNAME dans System.getenv().

    2. Exécutez hostname.exe et lisez la réponse.

  • Sur Linux

    1. Lisez la variable d’environnement HOSTNAME dans System.getenv()

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

    3. Lisez /etc/hostname (pour ce faire, j'exécute cat car l'extrait de code contient déjà du code à exécuter et à lire. Il serait préférable de lire simplement le fichier).

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 puisque echo $HOSTNAME renvoie le nom d’hôte correct, mais System.getenv("HOSTNAME") ne:

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 de l'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 de machines ont été remplacés mais j'ai gardé la capitalisation et la structure. Notez la nouvelle ligne supplémentaire lors de l'exécution de hostname, vous devrez peut-être en tenir compte dans certains cas.

50
Malt

InetAddress.getLocalHost().getHostName() est meilleur (comme l'explique Nick), mais pas très bon

Un hôte peut être connu sous plusieurs noms d'hôte différents. Généralement, vous recherchez le nom d'hôte de votre hôte dans un contexte spécifique.

Par exemple, dans une application Web, vous pouvez rechercher le nom d'hôte utilisé par celui qui a émis la demande que vous êtes en train de traiter. La meilleure façon de le trouver dépend du framework que vous utilisez pour votre application Web.

Dans certains types de services Internet, vous souhaitez que le nom d’hôte par lequel votre service est disponible soit accessible depuis l’extérieur. En raison des proxies, des pare-feu, etc., il se peut même que ce ne soit pas un nom d'hôte sur la machine sur laquelle votre service est installé. Vous pouvez essayer de définir un paramètre par défaut raisonnable, mais vous devez absolument le rendre configurable pour quiconque l'installe.

23
Arnout Engelen

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 définitions ici. La 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: cela nécessite souvent une recherche DNS, il est ambigu si l’hôte possède plusieurs interfaces réseau et c’est tout simplement un échec parfois (voir ci-dessous).

Mais sur tout OS, il y a aussi un autre nom. Nom de l'hôte défini très tôt dans le processus de démarrage, bien avant l'initialisation du réseau. Windows appelle cela nom_ordinateur, Linux l'appelle nom d'hôte du noya et Solaris utilise le mot nom_noeud. J'aime mieux le mot nom_ordinateur, je vais donc utiliser ce mot à partir de maintenant.

Trouver le nom de l'ordinateur

  • Sous Linux/Unix, le nom d’ordinateur correspond à ce que vous obtenez avec la commande gethostname() de la fonction C ou hostname de Shell ou la variable d’environnement HOSTNAME dans des shells de type Bash.

  • Sous Windows, le nom d’ordinateur correspond à ce que vous obtenez à partir de la variable d’environnement COMPUTERNAME ou de la fonction Win32 GetComputerName.

Java n'a aucun moyen d'obtenir ce que j'ai défini comme "nom_ordinateur". Bien sûr, il existe des solutions de contournement décrites dans d'autres réponses, comme pour l'appel de Windows System.getenv("COMPUTERNAME"), mais sous Unix/Linux, pas de bonne solution de contournement sans recourir à JNI/JNA ou à Runtime.exec(). Si vous ne craignez pas une solution JNI/JNA, alors il y a gethostname4j , ce qui est extrêmement simple et très facile à utiliser.

Passons maintenant à deux exemples, l'un sous Linux et l'autre sous Solaris, qui montrent comment vous pouvez facilement vous retrouver dans une situation dans laquelle vous ne pouvez pas obtenir le nom d'ordinateur à l'aide des méthodes standard Java.

Exemple Linux

Sur un système nouvellement créé, où l'hôte lors de l'installation a été nommé "chicago", nous modifions maintenant le nom d'hôte du noyau:

$ hostnamectl --static set-hostname dallas

Maintenant, le nom d'hôte du noyau est 'dallas', comme il ressort de 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 dans cela. Cela signifie simplement que le nom réseau de l'hôte (ou plutôt le nom de l'interface de bouclage) est différent du nom de l'ordinateur de l'hôte.

Maintenant, essayez d’exécuter InetAddress.getLocalHost().getHostName() et cela jettera 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 boucle <> nom_noeud.

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

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 de 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 lutter avec ça?

Très souvent, vous constaterez que InetAddress.getLocalHost().getHostName() renverra effectivement une valeur égale au nom de l'ordinateur. Donc, il n'y a pas de problème (à l'exception de la surcharge de résolution de nom).

Le problème se pose généralement dans les environnements PaaS où il existe une différence entre nom_ordinateur et nom de l'interface de bouclage. Par exemple, des personnes signalent des problèmes dans Amazon EC2.

Rapports de bug/RFE

Un peu de recherche révèle ce rapport RFE: link1 , link2 . Cependant, à en juger par les commentaires sur ce rapport, la question semble avoir été largement mal comprise par l'équipe du JDK, il est donc peu probable que le problème soit résolu.

J'aime la comparaison dans le RFE avec d'autres langages de programmation.

11
peterh

Les variables d'environnement peuvent également constituer 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 "supplémentaires" pour InetAddress.getLocalHost().getHostName(), car, comme plusieurs personnes le signalent, cette fonction ne fonctionne pas dans tous les environnements.

Runtime.getRuntime().exec("hostname") est un autre complé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;
11
Thomas W

Le moyen le plus portable d'obtenir le nom d'hôte de l'ordinateur actuel dans Java est le suivant:

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

Si vous n'êtes pas contre l'utilisation d'une dépendance externe à partir de maven central, j'ai écrit gethostname4j pour résoudre ce problème moi-même. Il utilise simplement la JNA pour appeler la fonction gethostname de libc (ou obtient le nom de l'ordinateur sous Windows) et vous le renvoie sous forme de chaîne.

https://github.com/mattsheppard/gethostname4j

2
Matt Sheppard