Cara yang disarankan untuk mendapatkan nama host di Jawa

275

Manakah dari berikut ini adalah cara terbaik dan paling portabel untuk mendapatkan nama host dari komputer saat ini di Jawa?

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

vs.

InetAddress.getLocalHost().getHostName()

Mahendra
sumber
Tumpukan teknologi apa ini?
tom redfern
Saya pikir satu-satunya nama yang didukung uname (uts_name) nyata adalah dari RMI / JMX VMID, tetapi ini adalah implementasi khusus.
eckes

Jawaban:

336

Sebenarnya - Anda tidak punya pilihan selain memanggil salah satu hostname(1)atau - di Unix gethostname(2). Ini adalah nama komputer Anda. Upaya apa pun untuk menentukan nama host oleh alamat IP seperti ini

InetAddress.getLocalHost().getHostName()

pasti gagal dalam beberapa keadaan:

  • Alamat IP mungkin tidak menyelesaikan nama apa pun. Pengaturan DNS yang buruk, pengaturan sistem yang buruk atau pengaturan penyedia yang buruk mungkin menjadi alasan untuk ini.
  • Nama dalam DNS dapat memiliki banyak alias yang disebut CNAME. Ini hanya dapat diselesaikan dalam satu arah dengan benar: nama ke alamat. Arah sebaliknya ambigu. Yang mana nama "resmi"?
  • Host dapat memiliki banyak alamat IP yang berbeda - dan setiap alamat dapat memiliki banyak nama yang berbeda. Dua kasus umum adalah: Satu port ethernet memiliki beberapa alamat IP "logis" atau komputer memiliki beberapa port ethernet. Dapat dikonfigurasi apakah mereka berbagi IP atau memiliki IP yang berbeda. Ini disebut "multihomed".
  • Satu Nama dalam DNS dapat diselesaikan ke beberapa Alamat IP. Dan tidak semua alamat itu harus berada di komputer yang sama! (Usecase: Suatu bentuk sederhana dari load-balancing)
  • Mari kita bahkan tidak mulai berbicara tentang alamat IP dinamis.

Juga jangan bingung nama alamat IP dengan nama host (nama host). Metafora mungkin membuatnya lebih jelas:

Ada kota besar (server) yang disebut "London". Di dalam tembok kota banyak bisnis terjadi. Kota ini memiliki beberapa gerbang (alamat IP). Setiap gerbang memiliki nama ("Gerbang Utara", "Gerbang Sungai", "Gerbang Southampton" ...) tetapi nama gerbangnya bukan nama kota tersebut. Anda juga tidak dapat menyimpulkan nama kota dengan menggunakan nama gerbang - "Gerbang Utara" akan menangkap setengah dari kota-kota besar dan bukan hanya satu kota. Namun - seorang asing (paket IP) berjalan di sepanjang sungai dan bertanya kepada seorang warga setempat: "Saya punya alamat aneh: 'Rivergate, kiri kedua, rumah ketiga'. Bisakah Anda membantu saya?" Warga setempat mengatakan: "Tentu saja, Anda berada di jalan yang benar, silakan saja dan Anda akan tiba di tempat tujuan dalam waktu setengah jam."

Ini menggambarkannya menurut saya.

Kabar baiknya adalah: The nyata hostname ini biasanya tidak diperlukan. Dalam kebanyakan kasus, nama apa pun yang berubah menjadi alamat IP pada host ini akan dilakukan. (Orang asing itu mungkin memasuki kota oleh Northgate, tetapi penduduk setempat yang membantu menerjemahkan bagian "ke-2".)

Jika kasus sudut yang tersisa Anda harus menggunakan definitif sumber pengaturan konfigurasi ini - yang merupakan fungsi C gethostname(2). Fungsi itu juga disebut oleh program hostname.

AH
sumber
9
Tidak cukup jawaban yang saya harapkan tetapi sekarang saya tahu bagaimana mengajukan pertanyaan yang lebih baik, terima kasih.
Sam Hasler
4
Itu adalah artikel bagus tentang keterbatasan yang dimiliki perangkat lunak apa pun (bukan hanya program Java) dalam menentukan nama host di dunia. Namun, perhatikan bahwa getHostName diimplementasikan dalam hal OS yang mendasarinya, mungkin dengan cara yang sama dengan nama host / gethostname. Pada sistem "normal", InetAddress.getLocalHost (). GetHostName () setara dengan memanggil nama host / gethostname, jadi Anda tidak bisa mengatakan bahwa satu gagal dengan cara yang lain tidak.
Peter Cardona
System.err.println (Runtime.getRuntime (). Exec ("hostname")); memberi saya ini: java.lang.UNIXProcess@6eb2384f
user152468
5
Implementasi InetAddress.getLocalHost (). GetHostName () sebenarnya sangat deterministik :) Jika Anda mengikuti kode sumber, ia akhirnya memanggil gethostname () di Windows, dan getaddrinfo () pada sistem Unixy. Hasilnya sama dengan menggunakan perintah hostname OS Anda. Sekarang nama host dapat memberikan jawaban yang tidak ingin Anda gunakan, itu dimungkinkan karena berbagai alasan. Secara umum, perangkat lunak harus mendapatkan nama host dari pengguna dalam file konfigurasi, dengan cara itu, selalu nama host yang benar. Anda bisa menggunakan InetAddress.getLocalhost (). GetHostName () sebagai default jika pengguna tidak memberikan nilai.
Greg
93

InetAddress.getLocalHost().getHostName() adalah cara yang lebih portabel.

exec("hostname")sebenarnya memanggil sistem operasi untuk menjalankan hostnameperintah.

Berikut adalah beberapa jawaban terkait lainnya pada SO:

EDIT: Anda harus melihat jawaban AH atau jawaban Arnout Engelen untuk perincian mengapa ini mungkin tidak berfungsi seperti yang diharapkan, tergantung pada situasi Anda. Sebagai jawaban untuk orang ini yang secara spesifik meminta portable, saya pikir masih getHostName()baik-baik saja, tetapi mereka memunculkan beberapa poin bagus yang harus dipertimbangkan.

Nick Knowlson
sumber
12
Menjalankan getHostName () menghasilkan kesalahan beberapa kali. Misalnya, di sini adalah apa yang saya dapatkan dalam sebuah contoh Amazon EC2 AMI Linux: java.net.UnknownHostException: Nama atau layanan yang tidak dikenal java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez
1
Juga, InetAddress.getLocalHost()dalam beberapa kasus mengembalikan perangkat loopback. Dalam hal itu, getHostName()mengembalikan "localhost", yang tidak terlalu berguna.
olenz
54

Seperti yang telah dicatat orang lain, mendapatkan nama host berdasarkan resolusi DNS tidak dapat diandalkan.

Karena pertanyaan ini sayangnya masih relevan pada tahun 2018 , saya ingin berbagi dengan Anda solusi yang tidak tergantung jaringan saya, dengan beberapa pengujian berjalan pada sistem yang berbeda.

Kode berikut mencoba melakukan hal berikut:

  • Di Windows

    1. Baca COMPUTERNAMEvariabel lingkungan melalui System.getenv().

    2. Jalankan hostname.exedan baca responsnya

  • Di Linux

    1. Baca HOSTNAMEvariabel lingkungan melaluiSystem.getenv()

    2. Jalankan hostnamedan baca responsnya

    3. Baca /etc/hostname(untuk melakukan ini saya mengeksekusi catkarena cuplikan sudah berisi kode untuk dieksekusi dan dibaca. Namun, membaca file akan lebih baik).

Kode:

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

Hasil untuk sistem operasi yang berbeda:

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 Yang ini agak aneh karena echo $HOSTNAMEmengembalikan nama host yang benar, tetapi System.getenv("HOSTNAME")tidak:

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

EDIT: Menurut legolas108 , System.getenv("HOSTNAME")berfungsi di Ubuntu 14.04 jika Anda menjalankan export HOSTNAMEsebelum mengeksekusi kode 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
"

Nama-nama mesin telah diganti tetapi saya tetap menggunakan huruf besar dan struktur. Perhatikan baris baru tambahan saat menjalankan hostname, Anda mungkin harus mempertimbangkannya dalam beberapa kasus.

Malt
sumber
3
Jika Anda export HOSTNAMEsebelum menjalankan kode Java pada Ubuntu 14.04 LTS juga dikembalikan oleh System.getenv("HOSTNAME").
legolas108
Anda harus secara eksplisit export HOSTNAMEagar Java menggunakannya? Saya pikir itu harus menjadi standar env var seperti PATH.
David
2
Ya, ini adalah salah satu bug Jawa yang tidak akan pernah diperbaiki karena alasan agama. Yang terkait adalah untuk dapat mengatur nama proses. Bug masuk hampir 20 tahun yang lalu.
Tuntable
Jawaban luar biasa, sangat teliti! Saya tidak tahu mengapa Anda menggunakan pembatas aneh, terutama mengingat setiap hasil cetak memiliki baris baru yang aneh di dalamnya. Apapun, saya memperbarui jawaban untuk bekerja dengan MacOS, mempersingkat indexOf untuk berisi panggilan, dan memperbaiki nama variabel OS untuk lebih konsisten dengan konvensi nama variabel standar.
Charlie
1
@Charlie \Apembatas hanyalah retasan untuk membaca seluruh InputStream menggunakan a Scanner.
Malt
24

InetAddress.getLocalHost().getHostName() lebih baik (seperti yang dijelaskan oleh Nick), tetapi masih belum terlalu bagus

Satu host dapat dikenal dengan banyak nama host yang berbeda. Biasanya Anda akan mencari nama host yang dimiliki host Anda dalam konteks tertentu.

Misalnya, dalam aplikasi web, Anda mungkin mencari nama host yang digunakan oleh siapa pun yang mengeluarkan permintaan yang sedang Anda tangani. Cara terbaik menemukan itu tergantung pada kerangka mana yang Anda gunakan untuk aplikasi web Anda.

Dalam beberapa jenis layanan lain yang berhubungan dengan internet, Anda akan menginginkan nama host layanan Anda tersedia dari 'luar'. Karena proksi, firewall, dll. Ini bahkan mungkin bukan nama host di mesin tempat layanan Anda diinstal - Anda mungkin mencoba membuat default yang masuk akal, tetapi Anda harus membuat ini dapat dikonfigurasi untuk siapa pun yang menginstalnya.

Arnout Engelen
sumber
18

Meskipun topik ini sudah dijawab, masih ada lagi yang bisa dikatakan.

Pertama-tama: Jelas kita perlu beberapa definisi di sini. The InetAddress.getLocalHost().getHostName()memberikan nama host seperti yang terlihat dari perspektif jaringan . Masalah dengan pendekatan ini didokumentasikan dengan baik dalam jawaban lain: sering membutuhkan pencarian DNS, itu ambigu jika host memiliki beberapa antarmuka jaringan dan itu kadang-kadang gagal kadang-kadang (lihat di bawah).

Tetapi pada OS apa pun ada nama lain juga. Nama host yang didefinisikan sangat awal dalam proses boot, jauh sebelum jaringan diinisialisasi. Jendela mengacu pada ini sebagai computername , Linux menyebutnya kernel nama host dan Solaris menggunakan kata nodename . Saya paling suka kata computername , jadi saya akan menggunakan kata itu mulai sekarang.

Menemukan nama pengguna

  • Di Linux / Unix, computername adalah apa yang Anda dapatkan dari fungsi C gethostname(), atau hostnameperintah dari shell atau HOSTNAMEvariabel lingkungan di shell mirip Bash.

  • Pada Windows, nama pengguna adalah apa yang Anda dapatkan dari variabel lingkungan COMPUTERNAMEatau GetComputerNamefungsi Win32 .

Java tidak memiliki cara untuk mendapatkan apa yang saya definisikan sebagai 'nama pengguna'. Tentu, ada solusi seperti yang dijelaskan dalam jawaban lain, seperti untuk panggilan Windows System.getenv("COMPUTERNAME"), tetapi pada Unix / Linux tidak ada solusi yang baik tanpa menggunakan JNI / JNA atau Runtime.exec(). Jika Anda tidak keberatan dengan solusi JNI / JNA maka ada gethostname4j yang mati sederhana dan sangat mudah digunakan.

Mari kita lanjutkan dengan dua contoh, satu dari Linux dan satu dari Solaris, yang menunjukkan bagaimana Anda dapat dengan mudah masuk ke situasi di mana Anda tidak dapat memperoleh nama pengguna menggunakan metode Java standar.

Contoh Linux

Pada sistem yang baru dibuat, tempat host selama instalasi dinamai 'chicago', kami sekarang mengubah apa yang disebut nama host kernel:

$ hostnamectl --static set-hostname dallas

Sekarang nama host kernel adalah 'dallas', seperti yang terlihat dari perintah hostname:

$ hostname
dallas

Tapi kita masih punya

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Tidak ada kesalahan konfigurasi dalam hal ini. Ini hanya berarti nama jaringan host (atau lebih tepatnya nama antarmuka loopback) berbeda dari nama pengguna host.

Sekarang, coba jalankan InetAddress.getLocalHost().getHostName() dan itu akan membuang java.net.UnknownHostException. Anda pada dasarnya terjebak. Tidak ada cara untuk mengambil nilai 'dallas' maupun nilai 'chicago'.

Contoh Solaris

Contoh di bawah ini didasarkan pada Solaris 11.3.

Tuan rumah telah sengaja dikonfigurasi sehingga nama loopback <> nama file.

Dengan kata lain kita memiliki:

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

dan isi / etc / hosts:

:1 chicago localhost
127.0.0.1 chicago localhost loghost

dan hasil dari perintah hostname adalah:

$ hostname
dallas

Sama seperti pada contoh Linux panggilan ke InetAddress.getLocalHost().getHostName()akan gagal

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

Sama seperti contoh Linux Anda sekarang terjebak. Tidak ada cara untuk mengambil nilai 'dallas' maupun nilai 'chicago'.

Kapan Anda akan benar-benar berjuang dengan ini?

Sangat sering Anda akan menemukan bahwa InetAddress.getLocalHost().getHostName()memang akan mengembalikan nilai yang sama dengan nama pengguna. Jadi tidak ada masalah (kecuali untuk penambahan resolusi nama).

Masalahnya muncul biasanya dalam lingkungan PaaS di mana ada perbedaan antara nama pengguna dan nama antarmuka loopback. Misalnya orang melaporkan masalah di Amazon EC2.

Laporan Bug / RFE

Sedikit pencarian mengungkapkan laporan RFE ini: link1 , link2 . Namun, menilai dari komentar pada laporan itu, masalah ini tampaknya telah banyak disalahpahami oleh tim JDK, sehingga tidak mungkin akan ditangani.

Saya suka perbandingan dalam RFE dengan bahasa pemrograman lain.

peterh
sumber
12

Variabel lingkungan juga dapat memberikan cara yang bermanfaat - COMPUTERNAMEpada Windows, HOSTNAMEpada sebagian besar shell Unix / Linux modern.

Lihat: https://stackoverflow.com/a/17956000/768795

Saya menggunakan ini sebagai metode "tambahan" untuk InetAddress.getLocalHost().getHostName(), karena seperti yang ditunjukkan beberapa orang, fungsi itu tidak berfungsi di semua lingkungan.

Runtime.getRuntime().exec("hostname")adalah suplemen lain yang mungkin. Pada tahap ini, saya belum menggunakannya.

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;
Thomas W
sumber
6
StringUtils? Apa itu?? (Saya tahu maksud Anda, saya pikir itu adalah karma buruk untuk membawa perpustakaan eksternal untuk tujuan tunggal ini .. dan di atas itu bahkan tidak menyebutkannya)
peterh
23
Adalah karma buruk untuk terus-menerus menulis cek "kosong" secara manual. Sangat banyak proyek yang pemeriksaannya dilakukan secara manual (seperti yang Anda sarankan) dan tidak konsisten, dengan banyak bug yang dihasilkan. Gunakan perpustakaan.
Thomas W
15
Seandainya ada yang melihat ini dan benar-benar bingung oleh StringUtils, itu disediakan oleh proyek commons-lang Apache. Sangat disarankan untuk menggunakannya.
JBCP
Entah bagaimana System.getenv("HOSTNAME")keluar nol pada Mac melalui Beanshell untuk Java, tetapi PATHdiekstraksi ok. Saya juga bisa melakukannya echo $HOSTNAMEdi Mac, hanya System.getenv ("HOSTNAME") tampaknya memiliki masalah. Aneh.
David
6

Cara paling portabel untuk mendapatkan nama host dari komputer saat ini di Jawa adalah sebagai berikut:

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);
    }
}
Desta Haileselassie Hagos
sumber
8
Selain portabilitas, bagaimana metode ini dibandingkan dengan metode dalam pertanyaan? Apa itu pro / kontra?
Marquez
4

Jika Anda tidak menentang menggunakan dependensi eksternal dari maven central, saya menulis gethostname4j untuk menyelesaikan masalah ini untuk diri saya sendiri. Itu hanya menggunakan JNA untuk memanggil fungsi gethostname libc (atau mendapatkan ComputerName di Windows) dan mengembalikannya kepada Anda sebagai string.

https://github.com/mattsheppard/gethostname4j

Matt Sheppard
sumber
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();
            }
        }
    }
}
ThuVien247.net
sumber
1
Apa manfaat dari metode ini?
Sam Hasler
1
Ini tidak valid, hanya memberikan nama NIC pertama yang bukan adaptor loopback.
Steve-o
sebenarnya ini bukan loopback pertama yang memiliki nama host ... belum tentu bukan loopback pertama.
Adam Gent
1

Hanya lintas satu ... lintas platform (Windows-Linux-Unix-Mac (Unix)) [Selalu berfungsi, Tidak diperlukan DNS]:

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

Kamu sudah selesai !!

Dan Ortega
sumber
InetAddress.getLocalHost (). GetHostName () tidak akan berfungsi di Unix?
P Satish Patro
1
Itu bekerja tetapi memerlukan resolusi dns, bagaimana jika Anda terhubung ke wifi tethering dari ponsel Anda (untuk menyebutkan hanya satu contoh) itu tidak akan memiliki DNS mengetahui mesin host lokal dan itu tidak akan berfungsi.
Dan Ortega
-2

InetAddress.getLocalHost (). GetHostName () adalah cara terbaik dari keduanya karena ini adalah abstraksi terbaik di tingkat pengembang.

java_mouse
sumber
Saya mencari jawaban yang berhubungan dengan satu host yang diketahui oleh banyak nama host.
Sam Hasler
@ SamHasler, apa situasi spesifik Anda? Seperti dijelaskan Arnout, ada berbagai cara tergantung pada apa yang Anda butuhkan.
Nick Knowlson