Javaでホスト名を取得する推奨方法


274

次のどれが、Javaで現在のコンピューターのホスト名を取得するための最良かつ最も移植可能な方法ですか

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

InetAddress.getLocalHost().getHostName()


これはどのテクノロジースタックですか?
トムレッドファーン2011

uname(uts_name)で裏付けられた実際の名前はRMI / JMX VMIDからのものだけだと思いますが、これは実装固有のものです。
15年

回答:


336

厳密に言うと- hostname(1)または-Unixで呼び出す以外に選択肢はありませんgethostname(2)。これはお使いのコンピュータの名前です。このようなIPアドレスでホスト名を特定しようとする試み

InetAddress.getLocalHost().getHostName()

いくつかの状況では失敗するはずです:

  • IPアドレスが名前に解決されない場合があります。DNSのセットアップ、システムのセットアップ、またはプロバイダーのセットアップが正しくないことが原因である可能性があります。
  • DNSの名前は、CNAMEと呼ばれる多くのエイリアスを持つことができます。これらは、一方向でのみ解決できます:名前からアドレスへ。逆方向があいまいです。「公式」の名前はどれですか?
  • ホストは多くの異なるIPアドレスを持つことができ、各アドレスは多くの異なる名前を持つことができます。2つの一般的なケースは次のとおりです。1つのイーサネットポートに複数の「論理」IPアドレスがあるか、コンピューターに複数のイーサネットポートがあります。IPを共有するか、異なるIPを持つかは構成可能です。これは「マルチホーム」と呼ばれます。
  • DNSの1つの名前は複数のIPアドレスに解決できます。そして、それらすべてのアドレスが同じコンピューター上にある必要はありません!(ユースケース:負荷分散の単純な形式)
  • 動的IPアドレスについて話し始めません。

また、IPアドレスの名前とホストの名前(ホスト名)を混同しないでください。比喩はそれをより明確にするかもしれません:

「ロンドン」と呼ばれる大都市(サーバー)があります。市壁の内側で多くのビジネスが起こります。市にはいくつかのゲート(IPアドレス)があります。各ゲートには名前があります(「ノースゲート」、「リバーゲート」、「サウサンプトンゲート」...)。ただし、ゲートの名前は都市の名前ではありません。また、ゲートの名前を使用して都市の名前を推測することはできません。「North Gate」は、1つの都市だけでなく、大都市の半分をキャッチします。しかし、見知らぬ人(IPパケット)が川沿いを歩いて地元の人に尋ねます。地元の人は言う:「もちろん、あなたは正しい道を進んでいます。先に進むだけで、30分以内に目的地に到着します。」

これは私が思うにそれをかなり示しています。

朗報です。通常、実際のホスト名は必要ありません。ほとんどの場合、このホストのIPアドレスに解決される名前であれば問題ありません。(見知らぬ人はノースゲートで街に入るかもしれませんが、役立つ地元の人々が「2番目の左」の部分を翻訳します。)

残りの場合は、この構成設定の最も信頼できるソース(C関数)を使用する必要がありますgethostname(2)。その関数はプログラムからも呼び出されhostnameます。


9
私が望んでいた答えではありませんが、今はもっと良い質問をする方法を知っています、ありがとう。
Sam Hasler、2011年


4
これは、(Javaプログラムだけでなく)任意のソフトウェアが世界でホストの名前を決定する際に持っている制限をうまく書いたものです。ただし、getHostNameは基礎となるOSに関して実装されていることに注意してください。おそらく、hostname / gethostnameと同じ方法です。「通常の」システムでは、InetAddress.getLocalHost()。getHostName()は、hostname / gethostnameを呼び出すことと同等であるため、実際には一方が失敗しないとは言えません。
Peter Cardona 2014年

System.err.println(Runtime.getRuntime()。exec( "hostname")); これをくれ:java.lang.UNIXProcess@6eb2384f
user152468

5
InetAddress.getLocalHost()。getHostName()の実装は実際には非常に確定的です:)ソースコードに従っている場合、最終的にはWindowsではgethostname()を呼び出し、Unixシステムではgetaddrinfo()を呼び出します。結果は、OSのhostnameコマンドを使用した場合と同じです。現在、ホスト名は、使用したくない回答を提供する場合がありますが、これは多くの理由で可能です。一般に、ソフトウェアは設定ファイルでユーザーからホスト名を取得する必要があります。これにより、常に正しいホスト名になります。ユーザーが値を指定しない場合、InetAddress.getLocalhost()。getHostName()をデフォルトとして使用できます。
グレッグ

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」を返しますが、あまり役に立ちません。
オレンツ

53

他の人が指摘したように、DNS解決に基づいてホスト名を取得することは信頼できません。

この質問は残念ながらまだ2018年にも関係があるので、いくつかの異なるシステムでテストを実行して、ネットワークに依存しないソリューションを共有したいと思います。

次のコードは次のことを試みます。

  • Windowsの場合

    1. からCOMPUTERNAME環境変数を読み取りますSystem.getenv()

    2. hostname.exe応答を実行して読み取る

  • Linuxの場合

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

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 LTSecho $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 で動作します。

Windows 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 HOSTNAMEUbuntu 14.04 LTSでJavaコードを実行する前であれば、それもによって返されSystem.getenv("HOSTNAME")ます。
legolas108

export HOSTNAMEJavaで明示的に使用する必要がありますか?私はそれがPATHのようなデフォルトのenv varであるべきだと思いました。
デビッド

2
はい、これはJavaのバグの1つであり、宗教的な理由で修正されることはありません。関連するのは、プロセス名を設定できるようにすることです。バグはほぼ20年前に記録されました。
調整可能2017年

非常に完全な素晴らしい答えです!なぜその奇妙な区切り文字を使用するのか、特にすべての印刷出力に奇妙な改行が含まれていることを考えると、わかりません。いずれにしても、MacOSで動作するように回答を更新し、indexOfを短くして呼び出しを含め、OS変数名を修正して、標準の変数名の規則との整合性を高めています。
チャーリー

1
@Charlie \A区切り文字は、を使用してInputStream全体を読み取るためのハックにすぎませんScanner
モルト

24

InetAddress.getLocalHost().getHostName() (ニックによって説明されているように)優れていますが、それでもあまり良くありません

1つのホストは、さまざまなホスト名で認識されます。通常、特定のコンテキストでホストが持っているホスト名を探します。

たとえば、Webアプリケーションでは、現在処理しているリクエストを発行した人が使用しているホスト名を探している可能性があります。最適な方法を見つけるには、Webアプリケーションに使用しているフレームワークに依存します。

他のある種のインターネット向けサービスでは、「外部」からサービスが利用できるホスト名が必要になります。プロキシ、ファイアウォールなどにより、これはサービスがインストールされているマシンのホスト名ではない可能性があります。妥当なデフォルトを考え出そうとするかもしれませんが、これをインストールする人は誰でもこれを構成可能にする必要があります。


18

このトピックについてはすでに回答済みですが、他にも言うべきことがあります。

まず第一に、明らかにここでいくつかの定義が必要です。には、ネットワークの観点から見InetAddress.getLocalHost().getHostName()たホストの名前が表示されます。このアプローチの問題は他の回答に詳しく記載されています。DNSルックアップが必要な場合が多く、ホストに複数のネットワークインターフェースがあり、単純に失敗する場合があります(以下を参照)。

しかし、どのOSにも別の名前があります。ブートプロセスの非常に早い段階で、ネットワークが初期化されるずっと前に定義されるホストの名前。Windowsではこれをcomputernameと呼び、Linuxではこれをカーネルホスト名と呼び、Solarisではnodenameという単語を使用します。Computernameという言葉が一番好きなので、これからこの言葉を使用します。

コンピュータ名を見つける

  • Linux / Unixでは、computernameはC関数から取得したものgethostname()、またはBashのようなhostnameシェルのシェルまたはHOSTNAME環境変数からのコマンドです。

  • Windowsでは、コンピューター名は環境変数COMPUTERNAMEまたはWin32 GetComputerName関数から取得したものです。

Javaには、「computername」として定義したものを取得する方法がありません。もちろん、Windowsの呼び出しのような他の回答に記載されている回避策はありますSystem.getenv("COMPUTERNAME")が、Unix / Linuxでは、JNI / JNAまたはに頼らずに適切な回避策はありませんRuntime.exec()。JNI / JNAソリューションを気にしない場合は、非常にシンプルで非常に使いやすいgethostname4jがあります。

1つはLinuxから、もう1つはSolarisから2つの例を見ていきましょう。これらは、標準のJavaメソッドを使用してコンピューター名を取得できない状況に簡単に入る方法を示しています。

Linuxの例

新しく作成されたシステムで、インストール中のホストの名前が「chicago」である場合、いわゆるカーネルホスト名を変更します。

$ hostnamectl --static set-hostname 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の例

以下の例は、Solaris 11.3に基づいています。

ホストは、ループバック名<>ノード名になるように意図的に構成されています。

つまり、次のようになります。

$ 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

Linuxの例のように、への呼び出し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レポート明らかにするリンク1リンク2を。ただし、そのレポートのコメントから判断すると、この問題はJDKチームによって大いに誤解されているように思われるため、対処される可能性は低いです。

RFEでの他のプログラミング言語との比較が好きです。


12

環境変数はCOMPUTERNAME、Windows、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
常に「空の」チェックを手動で書き込むことは悪いカルマです。非常に多くのプロジェクトで、このようなチェックが手動で(提案された方法で)行われており、一貫性がなく、多くのバグが発生しています。ライブラリを使用します。
トーマスW

15
誰かがこれを見て、実際にによって混乱しStringUtilsている場合、Apache commons-langプロジェクトによって提供されます。それ、またはそれに似たものを使用することを強くお勧めします。
JBCP 2014年

どういうわけかSystem.getenv("HOSTNAME")、JavaのBeanshellを介してMacでnullになりましたPATHが、正常に抽出されました。また、私は何ができるecho $HOSTNAMEだけでSystem.getenv(「HOSTNAME」)は、問題を持っているように見える、あまりにもMac上で。奇妙な。
デビッド

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
移植性はさておき、この方法は問題の方法とどのように比較されますか?長所/短所は何ですか?
マルケス

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
この方法の利点は何ですか?
サムハスラー

1
これは無効です。ループバックアダプターではない最初のNICの名前のみを提供します。
Steve-o

実際には、ホスト名を持つ最初の非ループバックです。必ずしも最初の非ループバックである必要はありません。
アダムゲント

1

ただ1つのライナー...クロスプラットフォーム(Windows-Linux-Unix-Mac(Unix))[常に機能し、DNSは不要]:

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

完了です!


InetAddress.getLocalHost()。getHostName()はUnixでは機能しませんか?
Pサティシュパトロ

1
これは機能しますが、DNS解決が必要です。電話からwifiテザリングに接続している場合(ほんの一例を挙げれば)、localhostマシンを認識するDNSがなく、機能しません。
Dan Ortega

-2

InetAddress.getLocalHost()。getHostName()は、開発者レベルでの最適な抽象化であるため、2つの方法のうち最良の方法です。


多くのホスト名で認識される1つのホストを処理する答えを探しています。
Sam Hasler

@SamHasler、あなたの特定の状況は何ですか?Arnoutが説明したように、必要なものに応じてさまざまな方法があります。
Nick Knowlson、2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.