Forma recomendada para obtener el nombre de host en Java

¿Cuál de las siguientes es la mejor y más portátil forma de obtener el nombre de host de la computadora actual en Java?

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

vs

InetAddress.getLocalHost().getHostName()

    Estrictamente hablando, no tiene más remedio que llamar a hostname(1) o – en Unix gethostname(2) . Este es el nombre de tu computadora. Cualquier bash de determinar el nombre de host por una dirección IP como esta

     InetAddress.getLocalHost().getHostName() 

    está destinado a fallar en algunas circunstancias:

    • La dirección IP puede no resolverse en ningún nombre. La mala configuración del DNS, la mala configuración del sistema o la mala configuración del proveedor pueden ser la razón de esto.
    • Un nombre en DNS puede tener muchos alias llamados CNAME. Estos solo se pueden resolver en una dirección correctamente: nombre a dirección. La dirección inversa es ambigua. ¿Cuál es el nombre “oficial”?
    • Un host puede tener muchas direcciones IP diferentes, y cada dirección puede tener muchos nombres diferentes. Dos casos comunes son: Un puerto de Ethernet tiene varias direcciones de IP “lógicas” o la computadora tiene varios puertos de Ethernet. Es configurable si comparten una IP o tienen IP diferentes. Esto se llama “multihomed”.
    • Un nombre en DNS puede resolver varias direcciones IP. ¡Y no todas esas direcciones deben estar ubicadas en la misma computadora! (Uso: una forma simple de equilibrio de carga)
    • No comencemos a hablar de direcciones IP dinámicas.

    Además, no confunda el nombre de una dirección IP con el nombre del host (nombre de host). Una metáfora podría aclararlo:

    Hay una gran ciudad (servidor) llamada “Londres”. Dentro de las murallas de la ciudad suceden muchos negocios. La ciudad tiene varias puertas (direcciones IP). Cada puerta tiene un nombre (“North Gate”, “River Gate”, “Southampton Gate” …) pero el nombre de la puerta no es el nombre de la ciudad. Tampoco se puede deducir el nombre de la ciudad usando el nombre de una puerta: “North Gate” atraparía la mitad de las ciudades más grandes y no solo una ciudad. Sin embargo, un extraño (paquete de IP) camina a lo largo del río y le pregunta a un lugareño: “Tengo una dirección extraña: ‘Rivergate, segunda a la izquierda, tercera casa’. ¿Pueden ayudarme?” El local dice: “Por supuesto, estás en el camino correcto, simplemente sigue adelante y llegarás a tu destino dentro de media hora”.

    Esto lo ilustra más o menos, creo.

    La buena noticia es que, por lo general, el nombre de host real no es necesario. En la mayoría de los casos, cualquier nombre que se resuelva en una dirección IP en este host hará. (El desconocido puede ingresar a la ciudad en Northgate, pero los locales que le resulten útiles traducen la parte “segunda a la izquierda”).

    Si se trata de casos de esquina restantes, debe usar la fuente definitiva de esta configuración, que es la función C gethostname(2) . Esa función también es llamada por el hostname progtwig.

    InetAddress.getLocalHost().getHostName() es la forma más portátil.

    exec("hostname") realmente llama al sistema operativo para ejecutar el comando de hostname .

    Aquí hay un par de otras respuestas relacionadas en SO:

    • ¿Nombre de la máquina actual de Java y usuario conectado?
    • Obtener el nombre DNS de la máquina local como se ve en una máquina remota

    EDITAR: Debería echar un vistazo a la respuesta de AH o la respuesta de Arnout Engelen para obtener detalles sobre por qué esto podría no funcionar como se esperaba, dependiendo de su situación. Como respuesta para esta persona que solicitó específicamente un dispositivo portátil, sigo creyendo que getHostName() está bien, pero sacan a relucir algunos puntos buenos que deberían tenerse en cuenta.

    Como otros han notado, obtener el nombre de host basado en la resolución de DNS no es confiable.

    Dado que esta pregunta todavía es relevante en 2018 , me gustaría compartir con ustedes mi solución independiente de la red, con algunas ejecuciones de prueba en diferentes sistemas.

    El siguiente código intenta hacer lo siguiente:

    • En Windows

      1. Lea la variable de entorno COMPUTERNAME través de System.getenv() .

      2. Ejecute hostname.exe y lea la respuesta

    • En Linux

      1. Lea la variable de entorno HOSTNAME través de System.getenv()

      2. Ejecutar hostname y leer la respuesta

      3. Lea /etc/hostname (para hacer esto, estoy ejecutando cat porque el fragmento ya contiene código para ejecutar y leer. Sin embargo, simplemente leer el archivo sería mejor).

    El código:

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

    Resultados para diferentes sistemas operativos:

    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 Este es un poco extraño ya que echo $HOSTNAME devuelve el nombre de host correcto, pero 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 " 

    EDITAR: según legolas108 , System.getenv("HOSTNAME") funciona en Ubuntu 14.04 si ejecuta export HOSTNAME antes de ejecutar el código de 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 " 

    Los nombres de las máquinas han sido reemplazados, pero mantuve las mayúsculas y la estructura. Tenga en cuenta la nueva línea adicional al ejecutar el hostname , es posible que deba tenerlo en cuenta en algunos casos.

    InetAddress.getLocalHost().getHostName() es mejor (como explica Nick), pero aún no es muy bueno

    Un host puede ser conocido bajo muchos nombres de host diferentes. Por lo general, buscará el nombre de host que tiene su host en un contexto específico.

    Por ejemplo, en una aplicación web, es posible que esté buscando el nombre de host utilizado por quien emitió la solicitud que está gestionando actualmente. La mejor forma de encontrar uno depende de qué marco está utilizando para su aplicación web.

    En algún tipo de otro servicio orientado a Internet, querrá que el nombre de host para el que está disponible su servicio sea desde el “exterior”. Debido a los proxies, cortafuegos, etc., es posible que ni siquiera sea un nombre de host en la máquina en la que está instalado su servicio; puede tratar de encontrar un valor predeterminado razonable, pero definitivamente debe ser configurable para quien instale esto.

    Las variables de entorno también pueden proporcionar un medio útil: COMPUTERNAME en Windows, HOSTNAME en la mayoría de las versiones modernas de Unix / Linux.

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

    Estoy usando estos métodos como “suplementarios” para InetAddress.getLocalHost().getHostName() , ya que, como señalan varias personas, esa función no funciona en todos los entornos.

    Runtime.getRuntime().exec("hostname") es otro posible complemento. En esta etapa, no lo he usado.

     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; 

    La forma más portátil de obtener el nombre de host de la computadora actual en Java es la siguiente:

     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); } } 

    Aunque este tema ya ha sido respondido, hay más que decir.

    Antes que nada: claramente necesitamos algunas definiciones aquí. InetAddress.getLocalHost().getHostName() le da el nombre del host como se ve desde una perspectiva de red . Los problemas con este enfoque están bien documentados en las otras respuestas: a menudo requiere una búsqueda de DNS, es ambiguo si el host tiene múltiples interfaces de red y simplemente falla algunas veces (ver a continuación).

    Pero en cualquier sistema operativo también hay otro nombre. Un nombre del host que se define muy temprano en el proceso de arranque, mucho antes de que la red se inicialice. Windows se refiere a esto como nombre de equipo , Linux lo llama kernel nombre de host y Solaris usa la palabra nombre de nodo . Me gusta más la palabra nombre de computadora , así que usaré esa palabra a partir de ahora.

    Encontrar el nombre de la computadora

    • En Linux / Unix, el nombre de la computadora es lo que obtienes de la función C gethostname() , o del comando hostname de la variable de entorno shell o HOSTNAME en los shells tipo Bash.

    • En Windows, el nombre del equipo es lo que obtienes de la variable de entorno COMPUTERNAME o de la función Win32 GetComputerName .

    Java no tiene forma de obtener lo que he definido como ‘nombre de computadora’. Claro, hay soluciones alternativas como se describe en otras respuestas, como Windows para llamar a System.getenv("COMPUTERNAME") , pero en Unix / Linux no hay una buena solución sin recurrir a JNI / JNA o Runtime.exec() . Si no te importa una solución JNI / JNA, entonces hay gethostname4j que es muy simple y muy fácil de usar.

    Avancemos con dos ejemplos, uno de Linux y el otro de Solaris, que demuestran cómo puede acceder fácilmente a una situación en la que no puede obtener el nombre de la computadora utilizando los métodos estándar de Java.

    Ejemplo de Linux

    En un sistema recién creado, donde el host durante la instalación ha sido nombrado como ‘chicago’, ahora cambiamos el llamado nombre de host kernel:

     $ hostnamectl --static set-hostname dallas 

    Ahora el nombre de host del kernel es ‘dallas’, como se desprende del comando hostname:

     $ hostname dallas 

    Pero todavía tenemos

     $ cat /etc/hosts 127.0.0.1 localhost 127.0.0.1 chicago 

    No hay una mala configuración en esto. Simplemente significa que el nombre de red del host (o más bien el nombre de la interfaz loopback) es diferente del nombre del equipo del host.

    Ahora, intente ejecutar InetAddress.getLocalHost().getHostName() y lanzará java.net.UnknownHostException. Básicamente estás atrapado. No hay forma de recuperar ni el valor ‘dallas’ ni el valor ‘chicago’.

    Ejemplo de Solaris

    El siguiente ejemplo se basa en Solaris 11.3.

    El host se ha configurado deliberadamente para que el nombre de bucle invertido sea <> nodename.

    En otras palabras, tenemos:

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

    y el contenido de / etc / hosts:

     :1 chicago localhost 127.0.0.1 chicago localhost loghost 

    y el resultado del comando de nombre de host sería:

     $ hostname dallas 

    Al igual que en el ejemplo de Linux, una llamada a InetAddress.getLocalHost().getHostName() fallará con

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

    Al igual que en el ejemplo de Linux, ahora estás atascado. No hay forma de recuperar ni el valor ‘dallas’ ni el valor ‘chicago’.

    ¿Cuándo realmente lucharás con esto?

    Muy a menudo encontrará que InetAddress.getLocalHost().getHostName() devolverá un valor que es igual al nombre de la computadora. Por lo tanto, no hay ningún problema (excepto por la sobrecarga adicional de la resolución del nombre).

    El problema surge típicamente en entornos PaaS donde hay una diferencia entre el nombre de la computadora y el nombre de la interfaz de bucle invertido. Por ejemplo, las personas informan problemas en Amazon EC2.

    Informes Bug / RFE

    Un poco de búsqueda revela este informe de RFE: link1 , link2 . Sin embargo, a juzgar por los comentarios sobre ese informe, el equipo de JDK parece haber malinterpretado en gran medida la cuestión, por lo que es poco probable que se aborde.

    Me gusta la comparación en RFE con otros lenguajes de progtwigción.

     hostName == null; Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); { while (interfaces.hasMoreElements()) { NetworkInterface nic = interfaces.nextElement(); Enumeration addresses = nic.getInetAddresses(); while (hostName == null && addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (!address.isLoopbackAddress()) { hostName = address.getHostName(); } } } } 

    Si no está en contra de usar una dependencia externa de maven central, escribí gethostname4j para resolver este problema yo mismo. Simplemente utiliza JNA para llamar a la función gethostname de libc (u obtiene ComputerName en Windows) y se lo devuelve como una cadena.

    https://github.com/mattsheppard/gethostname4j

    InetAddress.getLocalHost (). GetHostName () es la mejor manera de salir de los dos ya que esta es la mejor abstracción en el nivel de desarrollador.