Java로 호스트 이름을 얻는 권장 방법


274

다음 중 Java로 현재 컴퓨터의 호스트 이름을 얻는 가장 쉽고 이식 가능한 방법은 무엇입니까?

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

vs

InetAddress.getLocalHost().getHostName()


이것은 어떤 기술 스택입니까?
Tom Redfern

실제 uname (uts_name) 백업 이름은 RMI / JMX VMID의 유일한 이름이지만 구현에 따라 다릅니다.
eckes

답변:


336

엄밀히 말하면- hostname(1)또는 유닉스를 호출하는 것 외에는 선택의 여지가 없습니다 gethostname(2). 이것은 컴퓨터의 이름입니다. 이와 같은 IP 주소로 호스트 이름을 확인하려는 시도

InetAddress.getLocalHost().getHostName()

어떤 상황에서는 실패 할 것입니다 :

  • IP 주소는 어떤 이름으로도 해석되지 않을 수 있습니다. 잘못된 DNS 설정, 잘못된 시스템 설정 또는 잘못된 제공자 설정이 그 원인 일 수 있습니다.
  • DNS의 이름에는 CNAME이라는 많은 별칭이있을 수 있습니다. 이것은 한 방향으로 만 해결할 수 있습니다 : 이름에서 주소로. 반대 방향이 모호합니다. "공식"이름은 무엇입니까?
  • 호스트는 다른 IP 주소를 가질 수 있으며 각 주소는 다른 이름을 가질 수 있습니다. 두 가지 일반적인 경우는 다음과 같습니다. 하나의 이더넷 포트에 여러 "논리적"IP 주소가 있거나 컴퓨터에 여러 개의 이더넷 포트가 있습니다. IP를 공유하는지 또는 다른 IP를 가지고 있는지 구성 할 수 있습니다. 이것을 "멀티 홈"이라고합니다.
  • DNS에서 하나의 이름은 여러 IP 주소로 해석 될 수 있습니다. 이러한 주소가 모두 같은 컴퓨터에 있어야하는 것은 아닙니다! (사용 사례 : 간단한 형태의로드 밸런싱)
  • 동적 IP 주소에 대해서도 이야기하지 마십시오.

또한 IP 주소 이름과 호스트 이름 (호스트 이름)을 혼동하지 마십시오. 은유가 더 명확하게 만들 수 있습니다.

"런던"이라는 대도시 (서버)가 있습니다. 성벽 안에서 많은 사업이 일어납니다. 도시에는 여러 개의 게이트 (IP 주소)가 있습니다. 각 게이트에는 이름 ( "North Gate", "River Gate", "Southampton Gate"...)이 있지만 게이트 이름은 도시 이름이 아닙니다. 또한 게이트 이름을 사용하여 도시 이름을 추론 할 수 없습니다. "North Gate"는 한 도시가 아니라 큰 도시의 절반을 차지합니다. 그러나 낯선 사람 (IP 패킷)이 강을 따라 걸어 가서 현지인에게 다음과 같이 묻습니다. 현지인은 "물론 올바른 길을 가고 있습니다. 30 분 안에 목적지에 도착할 것입니다."라고 말합니다.

이것은 내가 생각하는 것을 거의 보여줍니다.

좋은 소식은 : 실제 호스트 이름은 일반적으로 필요하지 않습니다. 대부분의 경우이 호스트의 IP 주소로 확인되는 모든 이름이 사용됩니다. (이방인은 노스 게이트를 통해 도시로 들어올 수 있지만 도움이되는 지역 주민들은 "왼쪽 2 번째"부분을 번역합니다.)

나머지 코너 의 경우이 구성 설정 의 최종 소스 (C 기능)를 사용해야합니다 gethostname(2). 이 기능은 또한 프로그램에 의해 호출됩니다 hostname.


9
내가 원했던 대답은 아니지만 지금은 더 나은 질문을하는 법을 알고 있습니다.
Sam Hasler


4
이것은 Java 프로그램뿐만 아니라 모든 소프트웨어가 세계에서 호스트 이름을 결정할 때 갖는 제한 사항에 대한 훌륭한 글입니다. 그러나 getHostName은 기본 OS 측면에서 구현되며 아마도 호스트 이름 / gethostname과 같은 방식으로 구현됩니다. "정상적인"시스템에서 InetAddress.getLocalHost (). getHostName ()은 hostname / gethostname을 호출하는 것과 동일하므로 하나가 다른 하나가 실패하는 방식으로 실패한다고 말할 수는 없습니다.
Peter Cardona

System.err.println (Runtime.getRuntime (). exec ( "hostname")); java.lang.UNIXProcess@6eb2384f
user152468

5
InetAddress.getLocalHost (). getHostName ()의 구현은 실제로 매우 결정적입니다.) 소스 코드를 따르는 경우 궁극적으로 Windows에서는 gethostname ()을 호출하고 Unixy 시스템에서는 getaddrinfo ()를 호출합니다. 결과는 OS 호스트 이름 명령을 사용하는 것과 같습니다. 이제 호스트 이름은 사용하고 싶지 않은 답변을 제공 할 수 있으며 여러 가지 이유로 가능합니다. 일반적으로 소프트웨어는 구성 파일의 사용자로부터 호스트 이름을 가져와야합니다. 이렇게하면 항상 올바른 호스트 이름입니다. 사용자가 값을 제공하지 않으면 InetAddress.getLocalhost (). getHostName ()을 기본값으로 사용할 수 있습니다.
Greg Greg

93

InetAddress.getLocalHost().getHostName() 더 휴대하기 쉬운 방법입니다.

exec("hostname")실제로 운영 체제를 호출하여 hostname명령 을 실행합니다 .

SO에 대한 몇 가지 다른 답변이 있습니다.

편집 : 당신은 AH의 대답을 봐야합니다 상황에 따라 또는 Arnout Engelen의 답변 을 예상대로 작동하지 않는 이유에 대한 자세한 내용을 살펴보십시오. 특별히 휴대용을 요청한이 사람에 대한 답변으로, 나는 여전히 getHostName()괜찮다고 생각 하지만 고려해야 할 몇 가지 좋은 점을 제시합니다.


12
getHostName ()을 실행하면 몇 번 오류가 발생합니다. 예를 들어, Amazon EC2 AMI Linux 인스턴스에서 얻을 수있는 내용은 다음과 같습니다. java.net.UnknownHostException : 이름 또는 서비스를 알 수 없음 java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez

1
또한 InetAddress.getLocalHost()경우에 따라 루프백 장치를 반환합니다. 이 경우 getHostName()"localhost"를 반환하는데, 그다지 유용하지는 않습니다.
olenz

53

다른 사람들이 지적했듯이 DNS 확인을 기반으로 호스트 이름을 얻는 것은 신뢰할 수 없습니다.

이 질문은 불행히도 2018 년 에도 여전히 관련이 있기 때문에 다른 시스템에서 일부 테스트를 실행하여 네트워크 독립적 인 솔루션을 공유하고 싶습니다.

다음 코드는 다음을 수행하려고 시도합니다.

  • Windows에서

    1. COMPUTERNAME통해 환경 변수를 읽습니다 System.getenv().

    2. hostname.exe응답을 실행 하고 읽습니다.

  • 리눅스에서

    1. HOSTNAME통해 환경 변수를 읽으십시오System.getenv()

    2. hostname응답을 실행 하고 읽습니다.

    3. 읽기 /etc/hostname(이 cat코드는 이미 실행하고 읽을 코드가 포함되어 있기 때문에 실행 중입니다. 파일을 읽는 것이 더 좋을 것입니다).

코드:

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

다른 운영 체제에 대한 결과 :

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:""

오픈 수세 13.1

Unix-like computer name through env:"machinename"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:""

우분투 14.04 LTS 이것은 echo $HOSTNAME올바른 호스트 이름을 반환하기 때문에 다소 이상 하지만 다음과 같은 System.getenv("HOSTNAME")것은 아닙니다.

Unix-like computer name through env:"null"
Unix-like computer name through exec:"machinename
"
Unix-like computer name through /etc/hostname:"machinename
"

편집 : legolas108 에 따르면 Java 코드 System.getenv("HOSTNAME")를 실행 export HOSTNAME하기 전에 실행 하면 Ubuntu 14.04 에서 작동 합니다.

윈도우 7

Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"

윈도우 10

Windows computer name through env:"MACHINENAME"
Windows computer name through exec:"machinename
"

기계 이름이 바뀌었지만 대문자와 구조를 유지했습니다. 를 실행할 때 추가 줄 바꿈을 참고하십시오 hostname.


3
이 경우 export HOSTNAME이전에 우분투 14.04 LTS에서 Java 코드를 실행 그것은 또한에 의해 반환됩니다 System.getenv("HOSTNAME").
legolas108

export HOSTNAMEJava를 사용 하려면 명시 적 으로 사용해야합니까? PATH와 같은 기본 env var 여야한다고 생각했습니다.
David

2
예, 이것은 종교적인 이유로 고쳐지지 않을 Java 버그 중 하나입니다. 프로세스 이름을 설정할 수 있어야합니다. 거의 20 년 전에 버그가 기록되었습니다.
Tuntable

훌륭한 답변, 매우 철저한! 왜 이상한 구분 기호를 사용하는지 모르겠습니다. 특히 모든 인쇄 출력에 이상한 줄 바꿈이 있다고 생각합니다. 어쨌든 MacOS에서 작동하도록 응답을 업데이트하고 indexOf를 단축하여 호출을 포함하며 OS 변수 이름을 표준 변수 이름 규칙과 일치하도록 수정합니다.
찰리

1
@Charlie \A구분 기호 는를 사용하여 전체 InputStream을 읽는 해킹 일뿐 Scanner입니다.
Malt

24

InetAddress.getLocalHost().getHostName() (닉이 설명한대로) 더 좋지만 여전히 그리 좋지는 않습니다.

하나의 호스트는 여러 다른 호스트 이름으로 알려질 수 있습니다. 일반적으로 호스트는 특정 컨텍스트에서 호스트 이름을 찾습니다.

예를 들어, 웹 애플리케이션에서 현재 처리중인 요청을 발행 한 사람이 사용한 호스트 이름을 찾고있을 수 있습니다. 가장 적합한 방법은 웹 응용 프로그램에 사용중인 프레임 워크에 따라 다릅니다.

다른 종류의 인터넷 연결 서비스에서는 '외부'에서 서비스를 사용할 수있는 호스트 이름이 필요합니다. 프록시, 방화벽 등으로 인해 이것은 서비스가 설치된 시스템의 호스트 이름이 아닐 수도 있습니다. 합리적인 기본값을 설정하려고 시도 할 수 있지만이를 설치하는 사람은 반드시 구성 할 수 있어야합니다.


18

이 주제에 대해 이미 답변을 받았지만 더 많은 할 말이 있습니다.

우선 : 분명히 여기에 몇 가지 정의가 필요합니다. 는 InetAddress.getLocalHost().getHostName()당신에게 호스트의 이름을 제공 네트워크 관점에서 본 . 이 접근 방식의 문제점은 다른 답변에 잘 설명되어 있습니다. 종종 DNS 조회가 필요합니다. 호스트에 여러 개의 네트워크 인터페이스가 있고 때로는 실패 할 경우 모호합니다 (아래 참조).

그러나 모든 OS에는 다른 이름도 있습니다. 네트워크가 초기화되기 훨씬 전에 부트 프로세스 초기에 정의되는 호스트 이름. Windows는이를 컴퓨터 이름 으로 지칭하고 , Linux는이를 커널 호스트 이름이라고 하며 Solaris는 단어 nodename을 사용합니다 . 나는 computername 이라는 단어를 가장 좋아 하므로 앞으로 그 단어를 사용할 것입니다.

컴퓨터 이름 찾기

  • 리눅스 / 유닉스 컴퓨터 이름을 사용하면 C 함수에서 무엇을 얻을 gethostname(), 또는 hostname쉘 또는에서 명령 HOSTNAME쉘 강타 - 같은 환경 변수.

  • Windows에서 컴퓨터 이름은 환경 변수 COMPUTERNAME또는 Win32 GetComputerName기능 에서 얻는 것입니다 .

Java는 'computername'으로 정의한 것을 얻을 수있는 방법이 없습니다. 물론, Windows calling와 같은 다른 답변에 설명 된 해결 방법이 System.getenv("COMPUTERNAME")있지만 Unix / Linux에서는 JNI / JNA 또는에 의지하지 않으면 좋은 해결 방법이 없습니다 Runtime.exec(). JNI / JNA 솔루션이 마음에 들지 않으면 gethostname4j 가 있으며 간단하고 사용하기 쉽습니다.

표준 Java 메소드를 사용하여 컴퓨터 이름을 얻을 수없는 상황에 쉽게 도달 할 수있는 방법을 보여주는 Linux 및 Solaris의 두 가지 예를 살펴 보겠습니다.

리눅스 예

설치 중 호스트 이름이 'chicago'인 새로 작성된 시스템에서 이제 커널 호스트 이름을 변경합니다.

$ hostnamectl --static set-hostname dallas

이제 호스트 이름 명령에서 알 수 있듯이 커널 호스트 이름은 'dallas'입니다.

$ hostname
dallas

그러나 우리는 여전히

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

이 구성에는 잘못된 구성이 없습니다. 호스트의 네트워크 이름 (또는 루프백 인터페이스의 이름)이 호스트의 컴퓨터 이름과 다르다는 것을 의미합니다.

이제 실행을 시도 InetAddress.getLocalHost().getHostName() 하면 java.net.UnknownHostException이 발생합니다. 당신은 기본적으로 붙어 있습니다. 'dallas'또는 'chicago'값을 검색 할 방법이 없습니다.

솔라리스 예제

아래 예는 Solaris 11.3을 기반으로합니다.

루프백 이름 <> nodename이되도록 호스트가 일부러 구성되었습니다.

다시 말해 우리는

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

/ etc / hosts의 내용 :

:1 chicago localhost
127.0.0.1 chicago localhost loghost

hostname 명령의 결과는 다음과 같습니다.

$ hostname
dallas

리눅스 예제에서와 마찬가지로 호출 InetAddress.getLocalHost().getHostName()은 실패합니다.

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

Linux 예제와 마찬가지로 이제 막혔습니다. 'dallas'또는 'chicago'값을 검색 할 방법이 없습니다.

당신은 언제 정말로 이것과 싸울 것입니까?

종종 InetAddress.getLocalHost().getHostName()컴퓨터 이름과 같은 값을 반환 한다는 것을 알게 될 것입니다. 따라서 문제가 없습니다 (이름 확인의 추가 오버 헤드 제외).

이 문제는 일반적으로 컴퓨터 이름과 루프백 인터페이스 이름이 다른 PaaS 환경에서 발생합니다. 예를 들어 사람들은 Amazon EC2에서 문제를보고합니다.

버그 / RFE 보고서

약간의 검색으로이 RFE 보고서가 표시됩니다 ( link1 , link2) . 그러나 해당 보고서에 대한 의견을 바탕으로 JDK 팀은이 문제를 크게 오해 한 것으로 보이므로 해결되지 않을 것입니다.

RFE에서 다른 프로그래밍 언어와 비교하는 것이 좋습니다.


12

환경 변수는 COMPUTERNAMEWindows, HOSTNAME대부분의 최신 Unix / Linux 쉘 에서 유용한 수단을 제공 할 수도 있습니다 .

참조 : https://stackoverflow.com/a/17956000/768795

InetAddress.getLocalHost().getHostName()여러 사람들이 지적했듯이 모든 환경에서 해당 기능이 작동하지 않기 때문에 이를 "보완"방법으로 사용 하고 있습니다.

Runtime.getRuntime().exec("hostname")또 다른 가능한 보충제입니다. 이 단계에서는 사용하지 않았습니다.

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? 그게 뭐야 ?? (나는 당신이이 유일한 목적을 위해 외부 도서관을 가져 오는 것이 나쁜 업장이라고 생각한다는 것을 알고 있습니다. 그 위에 언급조차하지 않았습니다)
peterh

23
지속적으로 "빈"검사를 수동으로 작성하는 것은 업장이 좋지 않습니다. 매우 많은 프로젝트에서 이러한 검사를 수동으로 (권장하는 방식으로) 일관되게 수행하며 많은 버그가 발생합니다. 라이브러리를 사용하십시오.
Thomas W

15
누구나이 내용을보고 실제로 StringUtilsApache 혼동 -lang 프로젝트에서 제공하는 혼란 스럽습니다 . 그것을 사용하는 것이 좋습니다.
JBCP

System.getenv("HOSTNAME")Java에서 Beanshell을 통해 Mac에서 어떻게 든 null이 나왔지만 정상적으로 PATH추출되었습니다. echo $HOSTNAMEMac 에서도 할 수 있습니다 .System.getenv ( "HOSTNAME") 만 문제가있는 것 같습니다. 이상한.
David

6

Java로 현재 컴퓨터의 호스트 이름을 얻는 가장 쉬운 방법은 다음과 같습니다.

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
이식성 외에,이 방법은 질문의 방법과 어떻게 비교됩니까? 장단점은 무엇입니까?
Marquez

4

maven central의 외부 종속성을 사용하지 않으려면 gethostname4j를 작성 하여이 문제를 직접 해결하십시오. JNA를 사용하여 libc의 gethostname 함수를 호출하거나 Windows에서 ComputerName을 가져 와서 문자열로 반환합니다.

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
이 방법의 장점은 무엇입니까?
Sam Hasler

1
이것은 유효하지 않으며 루프백 어댑터가 아닌 첫 번째 NIC의 이름 만 제공합니다.
Steve-o

실제로 호스트 이름을 가진 첫 번째 비 루프백 ... 반드시 첫 번째 비 루프백은 아닙니다.
Adam Gent

1

한 줄짜리 ... 크로스 플랫폼 (Windows-Linux-Unix-Mac (Unix)) [항상 작동, DNS 필요 없음] :

String hostname = new BufferedReader(
    new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))
   .readLine();

끝났어 !!


InetAddress.getLocalHost (). getHostName ()은 Unix에서 작동하지 않습니까?
P Satish Patro

1
그것은 작동하지만 dns 해상도가 필요합니다. 휴대 전화의 와이파이 테 더링 (예를 들어 하나만 언급)에 연결하면 로컬 호스트 시스템을 알고있는 DNS가 없으며 작동하지 않습니다.
Dan Ortega

-2

InetAddress.getLocalHost (). getHostName ()은 개발자 수준에서 최고의 추상화이므로 둘 중 가장 좋은 방법입니다.


많은 호스트 이름으로 알고있는 하나의 호스트를 다루는 답변을 찾고 있습니다.
Sam Hasler

@ SamHasler, 특정 상황은 무엇입니까? Arnout이 설명했듯이 필요한 것에 따라 다른 방법이 있습니다.
Nick Knowlson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.