web-dev-qa-db-ja.com

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

次のうちどれがJavaで現在のコンピュータのホスト名を取得するための最善かつ最も移植性の高い方法ですか?

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

vs

InetAddress.getLocalHost().getHostName()

242
Mahendra

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

InetAddress.getLocalHost().getHostName()

状況によっては失敗するはずです。

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

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

"London"という大都市(サーバー)があります。街の壁の内側にはたくさんの商売があります。市にはいくつかのゲート(IPアドレス)があります。各門には名前( "North Gate"、 "River Gate"、 "Southampton Gate"など)がありますが、門の名前は市の名前ではありません。また、門の名前を使用して都市の名前を推測することはできません - "North Gate"は、1つの都市だけでなく、より大きな都市の半分を占めることになります。しかし - 見知らぬ人(IPパケット)が川に沿って歩き、地元の人に尋ねました。「私は奇妙な住所を持っています。地元の人によると、「もちろん、あなたは正しい道を進んでいます。ただ先に進むと、30分以内に目的地に到着します。」

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

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

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

304
A.H.

InetAddress.getLocalHost().getHostName()はより移植性の高い方法です。

exec("hostname")は、実際にはオペレーティングシステムを呼び出してhostnameコマンドを実行します。

これがSOに関する他の2つの関連する答えです。

編集:詳しくは AHの答え または Arnout Engelenの答え をご覧ください。あなたの状況によってはなぜこれが期待通りに動かないかもしれないかについて。特にポータブルを要求したこの人のための答えとして、私はまだgetHostName()が良いと思います、しかし彼らは考慮されるべきであるいくつかの良い点を思い出させます。

89
Nick Knowlson

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

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

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

  • Windowsの場合

    1. System.getenv()を通してCOMPUTERNAME環境変数を読みます。

    2. hostname.exeを実行してレスポンスを読む

  • Linuxの場合

    1. System.getenv()を通してHOSTNAME環境変数を読みます

    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 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コードを実行する前にexport HOSTNAMEを実行すると、System.getenv("HOSTNAME")はUbuntu 14.04で動作します。

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
"

マシン名は置き換えられましたが、私は大文字と小文字の区別をし続けました。 hostnameを実行するときの余分な改行に注意してください、場合によってはそれを考慮に入れなければならないかもしれません。

47
Malt

InetAddress.getLocalHost().getHostName()は(Nickによって説明されているように)優れていますが、まだそれほど良くはありません

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

たとえば、Webアプリケーションでは、現在処理している要求を発行した人が誰でも使用しているホスト名を探しているかもしれません。それを最もよく見つける方法は、Webアプリケーションにどのフレームワークを使用しているかによって異なります。

他のインターネット向けサービスでは、あなたのサービスが利用可能なホスト名を 'outside'から利用できるようにしたいでしょう。プロキシ、ファイアウォールなどのために、これはあなたのサービスがインストールされているマシン上のホスト名でさえないかもしれません - あなたは合理的なデフォルトを考え出そうと試みるかもしれません。

23
Arnout Engelen

このトピックはすでに回答されていますが、言うべきことがもっとあります。

まず第一に:ここでは明らかにいくつかの定義が必要です。 InetAddress.getLocalHost().getHostName()は、ネットワークの観点から見たホストの名前を示します。このアプローチの問題は他の答えでよく文書化されています:それはしばしばDNSルックアップを必要とします、ホストが複数のネットワークインターフェースを持っているならそれはあいまいです、そしてそれは時々明白に失敗します(下記参照)。

しかし、どのOSでも別の名前があります。ネットワークが初期化されるずっと前のブートプロセスの非常に早い段階で定義されるホストの名前。 Windowsはこれをコンピュータ名と呼び、Linuxはそれをカーネルホスト名と呼び、SolarisはWordノード名を使います。私はWordコンピュータ名が一番好きなので、これからWordを使います。

コンピューター名の検索

  • Linux/Unixでは、コンピュータ名は、Cの関数gethostname()、またはShellのhostnameコマンド、またはBash風シェルのHOSTNAME環境変数から取得したものです。

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

Javaには 'computername'として定義したものを取得する方法がありません。確かに、WindowsがSystem.getenv("COMPUTERNAME")を呼び出すような他の答えで説明されているような回避策はありますが、Unix/Linuxではありません。 JNI/JNAやRuntime.exec()に頼らずに良い回避策。あなたがJNI/JNAの解決策を気にしないのであれば、 gethostname4j があり、とても単純で使いやすいです。

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

Linuxの例

インストール中のホストが 'chicago'と命名された、新しく作成されたシステムでは、いわゆるカーネルホスト名を変更します。

$ hostnamectl --static set-hostname dallas

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()が実際にcomputernameと等しい値を返すことがよくあります。それで問題はありません(名前解決の追加されたオーバーヘッドを除いて)。

この問題は通常、computernameとloopbackインタフェースの名前に違いがあるPaaS環境で発生します。例えば、人々はAmazon EC2の問題を報告しています。

バグ/ RFEレポート

少し検索すると、このRFEレポートが明らかになります。 link1link2 。ただし、そのレポートに対するコメントから判断すると、この問題はJDKチームによって大きく誤解されているように思われるため、対処される可能性は低いです。

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

10
peterh

環境変数も有用な手段を提供するかもしれません - WindowsではCOMPUTERNAME、最近のほとんどのUnix/LinuxシェルではHOSTNAME

参照してください: 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;
10
Thomas W

現在のコンピュータのホスト名を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);
    }
}
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();
            }
        }
    }
}
3
ThuVien247.net

あなたがMaven Centralからの外部依存関係を使用することに反対でないならば、私は私自身のためにこの問題を解決するためにgethostname4jを書きました。 JNAを使用してlibcのgethostname関数を呼び出し(またはWindowsではComputerNameを取得し)、それを文字列として返します。

https://github.com/mattsheppard/gethostname4j

2
Matt Sheppard