Maneira recomendada de obter o nome do host em Java


275

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


Que pilha de tecnologia é essa?
— tom Redfern

Eu acho que o único nome real (unts_name) suportado é do RMI / JMX VMID, mas isso é específico da implementação.
— Eckes

Respostas:


336

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:

  • O endereço IP pode não ser resolvido em nenhum nome. Configuração de DNS incorreta, configuração incorreta do sistema ou configuração incorreta do provedor pode ser a razão disso.
  • Um nome no DNS pode ter muitos aliases chamados CNAMEs. Eles só podem ser resolvidos em uma direção corretamente: nome para o endereço. A direção inversa é ambígua. Qual é o nome "oficial"?
  • Um host pode ter muitos endereços IP diferentes - e cada endereço pode ter muitos nomes diferentes. Dois casos comuns são: Uma porta Ethernet possui vários endereços IP "lógicos" ou o computador possui várias portas Ethernet. É configurável se eles compartilham um IP ou têm IPs diferentes. Isso é chamado "multihomed".
  • Um nome no DNS pode resolver para vários endereços IP. E nem todos esses endereços devem estar localizados no mesmo computador! (Usecase: uma forma simples de balanceamento de carga)
  • Não vamos nem começar a falar sobre endereços IP dinâmicos.

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.


9
Não é exatamente a resposta que eu esperava, mas agora sei fazer uma pergunta melhor, obrigado.
— Sam Hasler


4
Essa é uma boa descrição das limitações que qualquer software (e não apenas um programa Java) possui na determinação do nome do host no mundo. No entanto, observe que getHostName é implementado em termos do sistema operacional subjacente, provavelmente da mesma forma que o nome do host / nome do host. Em um sistema "normal", InetAddress.getLocalHost (). GetHostName () é equivalente a chamar hostname / gethostname, portanto, você não pode realmente dizer que um falha de maneira que o outro não.
— Peter Cardona

System.err.println (Runtime.getRuntime (). Exec ("hostname")); me dá o seguinte: java.lang.UNIXProcess@6eb2384f
— user152468

5
A implementação de InetAddress.getLocalHost (). GetHostName () é realmente muito determinística :) Se você seguir o código-fonte, ele finalmente chamará gethostname () no Windows e getaddrinfo () nos sistemas Unixy. O resultado é o mesmo que usar o comando hostname do SO. Agora, o nome do host pode fornecer uma resposta que você não deseja usar, que é possível por vários motivos. Geralmente, o software deve obter o nome do host do usuário em um arquivo de configuração, assim, é sempre o nome do host correto. Você poderia usar InetAddress.getLocalhost (). GetHostName () como padrão se o usuário não fornecer um valor.
— Greg

93

InetAddress.getLocalHost().getHostName() é a maneira mais portátil.

exec("hostname")realmente chama o sistema operacional para executar o hostnamecomando.

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.


12
A execução de getHostName () resulta em um erro algumas vezes. Por exemplo, aqui está o que eu recebo em uma instância do Amazon EC2 AMI Linux: java.net.UnknownHostException: nome ou serviço não conhecido java.net.InetAddress.getLocalHost (InetAddress.java:1438)
— Marquez

1
Além disso, InetAddress.getLocalHost()em alguns casos, retorna o dispositivo de loopback. Nesse caso, getHostName()retorna "localhost", o que não é muito útil.
— olenz 21/07

54

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

    1. Leia a COMPUTERNAMEvariável de ambiente System.getenv().

    2. Execute hostname.exee leia a resposta

  • No Linux

    1. Leia a HOSTNAMEvariável de ambiente através deSystem.getenv()

    2. Execute hostnamee leia a resposta

    3. Read /etc/hostname(para fazer isso, estou executando, catjá 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 $HOSTNAMEretorna 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 HOSTNAMEantes 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.


3
Se você export HOSTNAMEantes de executar o código Java no Ubuntu 14.04 LTS, ele também será retornado por System.getenv("HOSTNAME").
— precisa saber é

Você precisa explicitamente export HOSTNAMEpara o Java usá-lo? Eu pensei que deveria ser um padrão var var como PATH.
— David

2
Sim, este é um dos erros do Java que nunca serão corrigidos por motivos religiosos. Um relacionado deve ser capaz de definir o nome do processo. Erros registrados quase 20 anos atrás.
— Tuntable

Excelente resposta, muito completa! Não sei por que você usa esse delimitador estranho, especialmente considerando que toda saída impressa possui uma nova linha estranha. Independentemente disso, estou atualizando a resposta para trabalhar com o MacOS, abreviando o indexOf para chamadas e corrigindo o nome da variável do SO para ser mais consistente com as convenções padrão de nomes de variáveis.
— 628 Charlie

1
@ Charlie, o \Adelimitador, é apenas um truque para ler todo o InputStream usando a Scanner.
— Malt

24

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.


18

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.

Localizando o nome do computador

  • No Linux / Unix, o nome do computador é o que você obtém da função C gethostname(), ou hostnamecomando do shell ou HOSTNAMEvariável de ambiente nos shells do tipo Bash.

  • No Windows, o nome do computador é o que você obtém da variável de ambiente COMPUTERNAMEou da GetComputerNamefunçã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.

Exemplo de Linux

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

Exemplo do Solaris

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

Quando você realmente lutará com isso?

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.

Relatórios de erros / RFE

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.


12

As variáveis ​​de ambiente também podem fornecer um meio útil - COMPUTERNAMEno Windows, HOSTNAMEna 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;

6
StringUtils? O que é isso?? (Eu sei o que você quer dizer que eu só acho que é mau karma para trazer uma biblioteca externa para este único propósito .. e em cima do que nem sequer mencioná-lo)
— peterh

23
É um karma ruim escrever constantemente cheques "vazios" manualmente. Muitos projetos têm essas verificações feitas manualmente (da maneira que você sugere) e inconsistentemente, com muitos erros resultantes. Use uma biblioteca.
— 21413 Thomas Thomas W

15
Caso alguém veja isso e fique realmente confuso StringUtils, é fornecido pelo projeto Apache commons-lang. É altamente recomendável usá-lo ou algo parecido.
— JBCP

De alguma forma, System.getenv("HOSTNAME")saiu nulo no Mac via Beanshell para Java, mas PATHfoi extraído ok. Eu também poderia fazer echo $HOSTNAMEno Mac, apenas System.getenv ("HOSTNAME") parece ter problema. Estranho.
— David

6

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

8
Portabilidade à parte, como esse método se compara ao método da pergunta? O que são prós / contras?
— Marquez

4

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.

https://github.com/mattsheppard/gethostname4j


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

1
Qual é o benefício desse método?
— Sam Hasler

1
Isso é inválido, ele fornece apenas o nome da primeira NIC que não é um adaptador de loopback.
— Steve-o

na verdade, é o primeiro não loopback que tem um nome de host ... não necessariamente o primeiro não loopback.
— Adam Gent

1

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 () não funcionará no Unix?
— P Satish Patro

1
Isso funciona, mas requer resolução DNS, e se você estiver conectado a uma conexão wifi do seu telefone (para mencionar apenas um exemplo), ele não terá um DNS conhecendo a máquina localhost e não funcionará.
— Dan Ortega

-2

InetAddress.getLocalHost (). GetHostName () é a melhor saída dentre as duas, pois essa é a melhor abstração no nível do desenvolvedor.


Estou procurando uma resposta que lida com um host conhecido por muitos nomes de host.
— Sam Hasler

@ SamHasler, qual é a sua situação específica? Como Arnout explicou, existem maneiras diferentes, dependendo do que você precisa.
— Nick Knowlson
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.