settitle - Manera recomendada para obtener el nombre de host en Java
para que sirve settitle en java (10)
¿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()
Aunque este tema ya ha sido respondido hay más que decir.
En primer lugar: claramente necesitamos algunas definiciones aquí. InetAddress.getLocalHost().getHostName()
le da el nombre del host visto 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 varias interfaces de red y a veces simplemente falla (ver más abajo).
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 se inicialice la red. Windows se refiere a esto como nombre de computadora , Linux lo llama nombre de host del kernel y Solaris usa la palabra nombre de nodo . Me gusta más la palabra nombre de computadora , así que usaré esa palabra de ahora en adelante.
Buscando el nombre de computadora
En Linux / Unix, el nombre de computadora es lo que se obtiene de la función C
gethostname()
, o el comando dehostname
de shell o la variable de entornoHOSTNAME
en shells similares a Bash.En Windows, el nombre de computadora es lo que se obtiene de la variable de entorno
COMPUTERNAME
o Win32GetComputerName
.
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 para Windows llamando a System.getenv("COMPUTERNAME")
, pero en Unix / Linux no hay una solución adecuada 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.
Continuemos con dos ejemplos, uno de Linux y otro de Solaris, que demuestran cómo se puede acceder fácilmente a una situación en la que no puede obtener el nombre de 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 del kernel:
$ hostnamectl --static set-hostname dallas
Ahora el nombre de host del kernel es ''dallas'', como se desprende del comando hostname:
$ hostname
dallas
Pero todavia tenemos
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
No hay mala configuración en esto. Solo significa que el nombre en red del host (o, más bien, el nombre de la interfaz de bucle de retorno) es diferente del nombre de computadora del host.
Ahora, intente ejecutar InetAddress.getLocalHost().getHostName()
y lanzará java.net.UnknownHostException. Básicamente estás atascado. 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 del bucle de retorno <> nombre de nodo.
En otras palabras tenemos:
$ svccfg -s system/identity:node listprop config
...
...
config/loopback astring chicago
config/nodename astring dallas
y los contenidos 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 el ejemplo de Linux, ahora estás atascado. No hay forma de recuperar ni el valor ''dallas'' ni el valor ''chicago''.
¿Cuándo lucharás realmente con esto?
Muy a menudo encontrará que InetAddress.getLocalHost().getHostName()
devolverá un valor que es igual al nombre de computadora. Así que no hay problema (a excepción de la sobrecarga agregada de la resolución de nombres).
El problema surge típicamente dentro de los entornos de PaaS donde hay una diferencia entre el nombre de computadora y el nombre de la interfaz de bucle invertido. Por ejemplo, las personas reportan problemas en Amazon EC2.
Informes de errores / RFE
Un poco de búsqueda revela este informe RFE: link1 , link2 . Sin embargo, a juzgar por los comentarios sobre ese informe, el equipo JDK parece haber entendido mal el problema, por lo que es poco probable que se aborde.
Me gusta la comparación en el RFE a otros lenguajes de programación.
Como han dicho otros, obtener el nombre de host basado en la resolución de DNS no es confiable.
Dado que esta pregunta, lamentablemente, sigue siendo relevante en 2018 , me gustaría compartir con ustedes mi solución independiente de la red, con algunas pruebas en diferentes sistemas.
El siguiente código intenta hacer lo siguiente:
En Windows
Lea la variable de entorno
COMPUTERNAME
través deSystem.getenv()
.Ejecute
hostname.exe
y lea la respuesta
En linux
Lea la variable de entorno
HOSTNAME
través deSystem.getenv()
Ejecutar el
hostname
y leer la respuestaLea
/etc/hostname
(para hacer esto, estoy ejecutandocat
ya que el fragmento de código ya contiene código para ejecutar y leer. Sin embargo, simplemente sería mejor leer el archivo).
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: De acuerdo con legolas108 , System.getenv("HOSTNAME")
funciona en Ubuntu 14.04 si ejecuta la export HOSTNAME
antes de ejecutar el código 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 la capitalización y la estructura. Tenga en cuenta la nueva línea adicional al ejecutar el hostname
, es posible que tenga que tenerlo en cuenta en algunos casos.
Estrictamente hablando, no tiene más remedio que llamar el hostname(1)
o - en Unix gethostname(2)
. Este es el nombre de tu computadora. Cualquier intento de determinar el nombre de host por una dirección IP como esta
InetAddress.getLocalHost().getHostName()
está obligado 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 pueden resolverse correctamente en una dirección: 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 Ethernet tiene varias direcciones IP "lógicas" o la computadora tiene varios puertos 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! (Caso de uso: una forma simple de equilibrio de carga)
- Ni siquiera vamos a empezar a hablar de direcciones IP dinámicas.
Tampoco confunda el nombre de una dirección IP con el nombre del host (nombre de host). Una metáfora podría dejarlo más claro:
Hay una gran ciudad (servidor) llamada "Londres". Dentro de las murallas de la ciudad pasan muchos negocios. La ciudad tiene varias puertas (direcciones IP). Cada puerta tiene un nombre ("Puerta Norte", "Puerta del Río", "Puerta Southampton" ...) pero el nombre de la puerta no es el nombre de la ciudad. Además, no puede deducir el nombre de la ciudad utilizando el nombre de una puerta: "Puerta Norte" atraparía la mitad de las ciudades más grandes y no solo una ciudad. Sin embargo, un extraño (paquete IP) camina a lo largo del río y le pregunta a un local: "Tengo una dirección extraña: ''Rivergate, segunda a la izquierda, tercera casa''. ¿Puede ayudarme?" El local dice: "Por supuesto, está en el camino correcto, simplemente continúe y llegará a su destino dentro de media hora".
Esto lo ilustra bastante, creo.
La buena noticia es: el nombre de host real generalmente no es necesario. En la mayoría de los casos, cualquier nombre que se resuelva en una dirección IP en este host servirá. (El extraño puede entrar a la ciudad por Northgate, pero los locales útiles traducen la parte "2da izquierda").
Si los casos de esquina restantes debe utilizar 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
programa.
InetAddress.getLocalHost (). GetHostName () es la mejor forma de salir de las dos, ya que esta es la mejor abstracción en el nivel de desarrollador.
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);
}
}
Las variables de entorno también pueden proporcionar un medio útil: COMPUTERNAME
en Windows, HOSTNAME
en la mayoría de los shells modernos de Unix / Linux.
Consulte: https://.com/a/17956000/768795
Los estoy utilizando como métodos "complementarios" 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;
Si no estás en contra de usar una dependencia externa de maven central, escribí gethostname4j para resolver este problema por mi cuenta. Simplemente utiliza JNA para llamar a la función gethostname de libc (o obtiene el Nombre de computadora en Windows) y se lo devuelve como una cadena.
InetAddress.getLocalHost().getHostName()
es la forma más portátil.
exec("hostname")
realidad llama al sistema operativo para ejecutar el comando de hostname
.
Aquí hay un par de otras respuestas relacionadas en SO:
- ¿El nombre de la máquina actual de Java y el usuario registrado?
- Obtenga el nombre DNS de la máquina local como lo ve 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 espera, dependiendo de su situación. Como respuesta para esta persona que solicitó específicamente un dispositivo portátil, sigo pensando que getHostName()
está bien, pero mencionan algunos puntos positivos que deben considerarse.
InetAddress.getLocalHost().getHostName()
es mejor (como lo 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, podría estar buscando el nombre de host utilizado por quien emitió la solicitud que está manejando actualmente. La mejor manera de encontrarlo depende del marco que esté utilizando para su aplicación web.
En algún otro tipo de servicio orientado a Internet, querrá que el nombre de host de su servicio esté disponible desde el "exterior". Debido a los proxies, firewalls, etc., esto podría no ser un nombre de host en la máquina en la que está instalado el servicio; puede intentar establecer un valor predeterminado razonable, pero definitivamente debería hacer esto configurable para quien lo instale.
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();
}
}
}
}