Qual das alternativas a seguir é a melhor e mais portátil maneira de obter o nome do host do computador atual em Java?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Qual das alternativas a seguir é a melhor e mais portátil maneira de obter o nome do host do computador atual em Java?
Runtime.getRuntime().exec("hostname")
vs
InetAddress.getLocalHost().getHostName()
Respostas:
A rigor - você não tem escolha a não ser ligar para hostname(1)
ou - no Unix gethostname(2)
. Este é o nome do seu computador. Qualquer tentativa de determinar o nome do host por um endereço IP como este
InetAddress.getLocalHost().getHostName()
está fadado a falhar em algumas circunstâncias:
Além disso, não confunda o nome de um endereço IP com o nome do host (hostname). Uma metáfora pode deixar mais claro:
Há uma grande cidade (servidor) chamada "London". Dentro das muralhas da cidade, muitos negócios acontecem. A cidade possui vários portões (endereços IP). Cada portão tem um nome ("North Gate", "River Gate", "Southampton Gate" ...), mas o nome do portão não é o nome da cidade. Além disso, você não pode deduzir o nome da cidade usando o nome de um portão - "North Gate" pegaria metade das cidades maiores e não apenas uma cidade. No entanto, um estranho (pacote IP) caminha ao longo do rio e pergunta a um morador local: "Eu tenho um endereço estranho: 'Rivergate, segundo à esquerda, terceira casa'. Você pode me ajudar?" O local diz: "Claro que você está no caminho certo, basta seguir em frente e chegará ao seu destino dentro de meia hora".
Isso ilustra muito bem, eu acho.
A boa notícia é: o nome do host real geralmente não é necessário. Na maioria dos casos, qualquer nome que se resolva em um endereço IP nesse host será suficiente. (O estrangeiro pode entrar na cidade por Northgate, mas moradores úteis traduzem a parte "2ª esquerda".)
Se os casos de canto restantes você deve usar a definitiva fonte desta configuração - que é a função C gethostname(2)
. Essa função também é chamada pelo programa hostname
.
InetAddress.getLocalHost().getHostName()
é a maneira mais portátil.
exec("hostname")
realmente chama o sistema operacional para executar o hostname
comando.
Aqui estão algumas outras respostas relacionadas ao SO:
EDIT: Você deve dar uma olhada na resposta da AH ou na resposta de Arnout Engelen para obter detalhes sobre por que isso pode não funcionar como o esperado, dependendo da sua situação. Como resposta para essa pessoa que solicitou especificamente o portátil, ainda acho que getHostName()
está bem, mas eles trazem alguns bons pontos que devem ser considerados.
InetAddress.getLocalHost()
em alguns casos, retorna o dispositivo de loopback. Nesse caso, getHostName()
retorna "localhost", o que não é muito útil.
Como outros observaram, a obtenção do nome do host com base na resolução do DNS não é confiável.
Como essa pergunta ainda é relevante em 2018 , gostaria de compartilhar com você minha solução independente de rede, com alguns testes em diferentes sistemas.
O código a seguir tenta fazer o seguinte:
No Windows
Leia a COMPUTERNAME
variável de ambiente System.getenv()
.
Execute hostname.exe
e leia a resposta
No Linux
Leia a HOSTNAME
variável de ambiente através deSystem.getenv()
Execute hostname
e leia a resposta
Read /etc/hostname
(para fazer isso, estou executando, cat
já que o trecho já contém código para executar e ler. Porém, basta ler o arquivo).
O 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 operacionais:
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 é meio estranho, pois echo $HOSTNAME
retorna o nome de host correto, mas System.getenv("HOSTNAME")
não:
Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"
EDIT: De acordo com legolas108 , System.getenv("HOSTNAME")
funciona no Ubuntu 14.04 se você executar export HOSTNAME
antes de executar o 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
"
Os nomes das máquinas foram substituídos, mas mantive a capitalização e a estrutura. Observe a nova linha extra ao executar hostname
, talvez seja necessário levar isso em consideração em alguns casos.
export HOSTNAME
antes de executar o código Java no Ubuntu 14.04 LTS, ele também será retornado por System.getenv("HOSTNAME")
.
export HOSTNAME
para o Java usá-lo? Eu pensei que deveria ser um padrão var var como PATH.
\A
delimitador, é apenas um truque para ler todo o InputStream usando a Scanner
.
InetAddress.getLocalHost().getHostName()
é melhor (como explicado por Nick), mas ainda não é muito bom
Um host pode ser conhecido sob vários nomes de host diferentes. Geralmente, você procurará o nome do host que seu host possui em um contexto específico.
Por exemplo, em um aplicativo Web, você pode estar procurando o nome do host usado por quem emitiu a solicitação que você está manipulando no momento. A melhor maneira de descobrir que isso depende de qual estrutura você está usando para seu aplicativo da web.
Em algum tipo de outro serviço voltado para a Internet, você deseja que o nome do host esteja disponível através do 'fora'. Devido a proxies, firewalls, etc., pode até não ser um nome de host na máquina em que o serviço está instalado - você pode tentar obter um padrão razoável, mas definitivamente deve configurá-lo para quem o instalar.
Embora este tópico já tenha sido respondido, há mais a dizer.
Primeiro de tudo: Claramente, precisamos de algumas definições aqui. O InetAddress.getLocalHost().getHostName()
fornece o nome do host como visto da perspectiva da rede . Os problemas com essa abordagem estão bem documentados nas outras respostas: geralmente requer uma pesquisa de DNS, é ambíguo se o host tiver várias interfaces de rede e simplesmente falhar algumas vezes (veja abaixo).
Mas em qualquer sistema operacional também existe outro nome. Um nome do host que é definido muito cedo no processo de inicialização, muito antes da inicialização da rede. O Windows se refere a isso como nome do computador , o Linux chama de nome do host do kernel e o Solaris usa a palavra nome do nó . Eu gosto mais da palavra nomedocomputador , então usarei essa palavra a partir de agora.
No Linux / Unix, o nome do computador é o que você obtém da função C gethostname()
, ou hostname
comando do shell ou HOSTNAME
variável de ambiente nos shells do tipo Bash.
No Windows, o nome do computador é o que você obtém da variável de ambiente COMPUTERNAME
ou da GetComputerName
função Win32 .
Java não tem como obter o que defini como 'computername'. Claro, existem soluções alternativas, conforme descrito em outras respostas, como para chamadas do Windows System.getenv("COMPUTERNAME")
, mas no Unix / Linux não há uma boa solução alternativa sem recorrer ao JNI / JNA ou Runtime.exec()
. Se você não se importa com uma solução JNI / JNA, existe o gethostname4j que é simples e muito fácil de usar.
Vamos seguir com dois exemplos, um do Linux e outro do Solaris, que demonstram como você pode facilmente entrar em uma situação em que não pode obter o nome do computador usando métodos Java padrão.
Em um sistema recém-criado, em que o host durante a instalação foi nomeado como 'chicago', agora alteramos o chamado nome do host do kernel:
$ hostnamectl --static set-hostname dallas
Agora, o nome do host do kernel é 'dallas', como é evidente no comando hostname:
$ hostname
dallas
Mas ainda temos
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 chicago
Não há configuração incorreta nisso. Significa apenas que o nome da rede do host (ou melhor, o nome da interface de loopback) é diferente do nome do computador do host.
Agora, tente executar InetAddress.getLocalHost().getHostName()
e ele lançará java.net.UnknownHostException. Você está basicamente preso. Não há como recuperar nem o valor 'dallas' nem o valor 'chicago'.
O exemplo abaixo é baseado no Solaris 11.3.
O host foi deliberadamente configurado para que o nome do loopback <> nodename.
Em outras palavras, temos:
$ svccfg -s system/identity:node listprop config
...
...
config/loopback astring chicago
config/nodename astring dallas
e o conteúdo de / etc / hosts:
:1 chicago localhost
127.0.0.1 chicago localhost loghost
e o resultado do comando hostname seria:
$ hostname
dallas
Assim como no exemplo do Linux, uma chamada para InetAddress.getLocalHost().getHostName()
falhará com
java.net.UnknownHostException: dallas: dallas: node name or service name not known
Assim como no exemplo do Linux, você está preso agora. Não há como recuperar nem o valor 'dallas' nem o valor 'chicago'.
Muitas vezes você descobrirá que InetAddress.getLocalHost().getHostName()
realmente retornará um valor igual ao nome do computador. Portanto, não há problema (exceto a sobrecarga adicional da resolução de nomes).
O problema geralmente ocorre em ambientes PaaS, nos quais há uma diferença entre o nome do computador e o nome da interface de loopback. Por exemplo, as pessoas relatam problemas no Amazon EC2.
Um pouco de pesquisa revela este relatório do RFE: link1 , link2 . No entanto, a julgar pelos comentários nesse relatório, o problema parece ter sido amplamente mal compreendido pela equipe do JDK, portanto, é improvável que seja abordado.
Gosto da comparação no RFE com outras linguagens de programação.
As variáveis de ambiente também podem fornecer um meio útil - COMPUTERNAME
no Windows, HOSTNAME
na maioria dos shells Unix / Linux modernos.
Consulte: https://stackoverflow.com/a/17956000/768795
Estou usando esses métodos como "suplementares" para InetAddress.getLocalHost().getHostName()
, como várias pessoas apontam, essa função não funciona em todos os ambientes.
Runtime.getRuntime().exec("hostname")
é outro complemento possível. Nesta fase, eu não o usei.
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
, é fornecido pelo projeto Apache commons-lang. É altamente recomendável usá-lo ou algo parecido.
System.getenv("HOSTNAME")
saiu nulo no Mac via Beanshell para Java, mas PATH
foi extraído ok. Eu também poderia fazer echo $HOSTNAME
no Mac, apenas System.getenv ("HOSTNAME") parece ter problema. Estranho.
A maneira mais portátil de obter o nome do host do computador atual em Java é a seguinte:
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);
}
}
Se você não é contra o uso de uma dependência externa da maven central, escrevi gethostname4j para resolver esse problema por conta própria. Ele apenas usa o JNA para chamar a função gethostname da libc (ou obtém o ComputerName no Windows) e a retorna como uma string.
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();
}
}
}
}
Apenas uma linha ... multiplataforma (Windows-Linux-Unix-Mac (Unix)) [Sempre funciona, sem necessidade de DNS]:
String hostname = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
.readLine();
Você Terminou !!
InetAddress.getLocalHost (). GetHostName () é a melhor saída dentre as duas, pois essa é a melhor abstração no nível do desenvolvedor.