Java'da ana bilgisayar adı almanın önerilen yolu


274

Aşağıdakilerden hangisi Java'da mevcut bilgisayarın ana bilgisayar adını almanın en iyi ve en taşınabilir yoludur?

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

vs

InetAddress.getLocalHost().getHostName()


Bu hangi teknoloji yığını?
tom redfern

Ben sadece gerçek uname (uts_name) destekli adı RMI / JMX VMID olduğunu düşünüyorum, ama bu uygulamaya özgüdür.
eckes

Yanıtlar:


336

Kesinlikle konuşmak gerekirse - hostname(1)Unix'te ya da --'dan birini çağırmaktan başka seçeneğiniz yok gethostname(2). Bu bilgisayarınızın adıdır. Ana bilgisayar adını böyle bir IP adresiyle belirleme girişimi

InetAddress.getLocalHost().getHostName()

bazı durumlarda başarısızlığa mahkumdur:

  • IP adresi herhangi bir ada çözümlenmeyebilir. Kötü DNS kurulumu, kötü sistem kurulumu veya kötü sağlayıcı kurulumu bunun nedeni olabilir.
  • DNS'deki bir adda CNAME adı verilen birçok takma ad olabilir. Bunlar yalnızca tek bir yönde düzgün bir şekilde çözülebilir: addan adrese. Ters yön belirsizdir. Hangisi "resmi" isim?
  • Bir ana bilgisayarın birçok farklı IP adresi olabilir ve her adresin birçok farklı adı olabilir. İki yaygın durum şunlardır: Bir ethernet bağlantı noktasında birkaç "mantıksal" IP adresi veya bilgisayarın birkaç ethernet bağlantı noktası vardır. IP paylaşıp paylaşmadıkları veya farklı IP'leri olup olmadıkları yapılandırılabilir. Buna "çoklu ev" denir.
  • DNS'deki bir Ad birkaç IP Adresine çözümlenebilir. Ve bu adreslerin hepsi aynı bilgisayarda bulunmamalıdır! (Usecase: Basit bir yük dengeleme şekli)
  • Dinamik IP adresleri hakkında konuşmaya bile başlamayalım.

Ayrıca bir IP adresinin adını ana bilgisayar adıyla (ana bilgisayar adı) karıştırmayın. Bir metafor bunu daha açık hale getirebilir:

"Londra" adında büyük bir şehir (sunucu) var. Surların içinde çok iş var. Şehrin birkaç kapısı vardır (IP adresleri). Her kapının bir adı vardır ("Kuzey Kapısı", "Nehir Kapısı", "Southampton Kapısı" ...) ama kapının adı şehrin adı değildir. Ayrıca bir kapı adını kullanarak şehrin adını çıkaramazsınız - "Kuzey Kapısı" sadece bir şehri değil, büyük şehirlerin yarısını yakalardı. Ancak - bir yabancı (IP paketi) nehir boyunca yürür ve yerel bir sorar: "Garip bir adresim var: 'Rivergate, ikinci sol, üçüncü ev'. Bana yardım edebilir misin?" Yerel diyor ki: "Elbette, doğru yoldasınız, sadece devam edin ve hedefinize yarım saat içinde varacaksınız."

Sanırım bunu neredeyse gösteriyor.

İyi haber şu: Gerçek ana bilgisayar adı genellikle gerekli değildir. Çoğu durumda, bu ana bilgisayarda bir IP adresine çözümlenen herhangi bir ad kullanılır. (Yabancı şehre Northgate tarafından girebilir, ancak yardımsever yerliler "2. sol" kısmı tercüme ederler.)

Kalan köşe durumları varsa , bu yapılandırma ayarının kesin kaynağını kullanmalısınız - C işlevi gethostname(2). Bu fonksiyon program tarafından da çağrılır hostname.


9
Tam olarak umduğum cevap değil ama şimdi nasıl daha iyi bir soru soracağımı biliyorum, teşekkürler.
Sam Hasler


4
Bu, herhangi bir yazılımın (sadece bir Java programı değil) dünyadaki ana bilgisayarın adını belirlemedeki sınırlamalarının güzel bir yazımıdır. Ancak, getHostName öğesinin, muhtemelen hostname / gethostname ile aynı şekilde, temel işletim sistemi açısından uygulandığını unutmayın. "Normal" bir sistemde, InetAddress.getLocalHost (). GetHostName (), hostname / gethostname öğesini çağırmakla eşdeğerdir, bu yüzden birinin diğerinin yapmadığı şekilde başarısız olduğunu söyleyemezsiniz.
Peter Cardona

System.err.println (Runtime.getRuntime () exec ( "ana bilgisayar").); bana bunu verir: java.lang.UNIXProcess@6eb2384f
user152468

5
InetAddress.getLocalHost (). GetHostName () uygulaması çok belirleyicidir :) Kaynak kodunu izlerseniz, sonuç olarak Windows'ta gethostname () ve Unixy sistemlerinde getaddrinfo () 'u çağırır. Sonuç, OS ana bilgisayar adı komutunuzu kullanmakla aynıdır. Artık anasistem adı kullanmak istemediğiniz bir yanıt sağlayabilir, bu birçok nedenden dolayı mümkündür. Genellikle, yazılım ana bilgisayar adını bir yapılandırma dosyasında kullanıcıdan almalıdır, bu şekilde her zaman doğru ana bilgisayar adı olur. Kullanıcı bir değer sağlamazsa, varsayılan olarak InetAddress.getLocalhost (). GetHostName () yöntemini kullanabilirsiniz.
Greg

93

InetAddress.getLocalHost().getHostName() daha taşınabilir bir yoldur.

exec("hostname")aslında hostnamekomutu çalıştırmak için işletim sistemini çağırır .

İşte SO ile ilgili diğer birkaç cevap:

DÜZENLEME: Durumunuza bağlı olarak bunun neden beklendiği gibi çalışmayabileceğine ilişkin ayrıntılar için AH'nin cevabına veya Arnout Engelen'in cevabına bir göz atmalısınız . Özellikle portatif talep eden bu kişi için bir cevap olarak, hala iyi olduğunu düşünüyorum getHostName(), ancak dikkate alınması gereken bazı iyi noktalar getiriyorlar.


12
GetHostName () öğesini çalıştırmak bazen hatalarla sonuçlanır. Örneğin, bir Amazon EC2 AMI Linux örneğinde aldığım şey: java.net.UnknownHostException: İsim veya hizmet bilinmiyor java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez

1
Ayrıca, InetAddress.getLocalHost()bazı durumlarda geri döngü aygıtı döndürür. Bu durumda, getHostName()çok yararlı olmayan "localhost" döndürür.
olenz

53

Diğerlerinin de belirttiği gibi, ana bilgisayar adını DNS çözümlemesine göre almak güvenilir değildir.

Bu soru maalesef 2018'de hala alakalı olduğundan , farklı sistemlerde bazı test çalışmalarıyla ağdan bağımsız çözümümü sizinle paylaşmak istiyorum.

Aşağıdaki kod aşağıdakileri yapmaya çalışır:

  • Windows'ta

    1. COMPUTERNAMEOrtam değişkenini aracılığıyla okuyun System.getenv().

    2. hostname.exeYanıtı yürütün ve okuyun

  • Linux'ta

    1. HOSTNAMEOrtam değişkenini şu yolla okuyun:System.getenv()

    2. hostnameYanıtı yürütün ve okuyun

    3. Oku /etc/hostname(bunu yapmak için yürütüyorumcat için snippet zaten yürütmek ve okumak için kod içerdiğinden . Sadece dosyayı okumak daha iyi olsa da).

Kod:

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

Farklı işletim sistemleri için sonuçlar:

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 Doğru ana bilgisayar adını döndürdüğü için bu tür bir garip , ancak System.getenv("HOSTNAME")şunları yapmıyor:

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

DÜZENLEME: Legolas108'e göre ,System.getenv("HOSTNAME") koşarsan Ubuntu 14.04 üzerinde çalışır export HOSTNAMEJava kodunu çalıştırmadan önce.

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
"

Makine adları değiştirildi ancak büyük / küçük harf kullanımını ve yapısını korudum. Yürütürken ekstra satırsonuna dikkat edin, hostnamebazı durumlarda bunu dikkate almanız gerekebilir.


3
Eğer varsa export HOSTNAMEönce Ubuntu 14.04 LTS Java kodu çalıştırarak aynı zamanda tarafından döndürülür System.getenv("HOSTNAME").
legolas108

export HOSTNAMEJava'nın kullanması için açıkça mi? PATH gibi varsayılan bir env var olması gerektiğini düşündüm.
David

2
Evet, bu dini nedenlerle asla düzeltilmeyecek Java hatalarından biridir. İlgili bir işlem adının belirlenebilmesidir. Hatalar neredeyse 20 yıl önce günlüğe kaydedildi.
Tuntable

Mükemmel cevap, çok kapsamlı! Bu tuhaf sınırlayıcıyı neden kullandığınızı bilmiyorum, özellikle de her basılı çıktıda garip bir satırsonu bulunduğunu düşünürsek. Ne olursa olsun, MacOS ile çalışmak için cevabı güncelliyoruz, indexOf'u çağrıları içerecek şekilde kısaltıyorum ve OS değişken adını standart değişken adı kurallarıyla daha tutarlı olacak şekilde düzeltiyorum.
Charlie

1
@Charlie \Asınırlayıcı sadece a kullanarak tüm InputStream okumak için bir hack Scanner.
Malt

24

InetAddress.getLocalHost().getHostName() (Nick'in açıkladığı gibi) daha iyi, ama yine de çok iyi değil

Bir ana makine birçok farklı ana makine adı altında bilinir. Genellikle, barındırıcınızın belirli bir bağlamda sahip olduğu ana bilgisayar adını ararsınız.

Örneğin, bir web uygulamasında, şu anda yürütmekte olduğunuz isteği veren kişi tarafından kullanılan ana bilgisayar adını arıyor olabilirsiniz. Bunu en iyi nasıl bulacağınız, web uygulamanız için hangi çerçeveyi kullandığınıza bağlıdır.

İnternete yönelik başka bir tür hizmette, hizmetinizin ana bilgisayar adının 'dışarıdan' erişilebilir olmasını istersiniz. Proxy'ler, güvenlik duvarları vb. Nedeniyle, hizmetinizin yüklü olduğu makinede bir ana makine adı bile olmayabilir - makul bir varsayılan bulmaya çalışabilirsiniz, ancak bunu yükleyen herkes için kesinlikle yapılandırılabilir hale getirmelisiniz.


18

Bu konu zaten cevaplanmış olsa da söylenecek daha çok şey var.

Her şeyden önce: Burada bazı tanımlara ihtiyacımız var. InetAddress.getLocalHost().getHostName()Size konağın adını veren bir ağ perspektifinden görüldüğü gibi . Bu yaklaşımla ilgili sorunlar diğer cevaplarda iyi belgelenmiştir: genellikle bir DNS araması gerektirir, ana bilgisayarın birden çok ağ arabirimi varsa belirsizdir ve bazen basitçe başarısız olur (aşağıya bakın).

Ancak herhangi bir işletim sisteminde başka bir isim de var. Ağ başlatılmadan çok önce, önyükleme işleminde çok erken tanımlanan ana bilgisayarın adı. , Windows olarak adlandırır bilgisayaradı , Linux onu çağırır Hostadı çekirdek ve Solaris kelime kullanır nodename . Bilgisayar adı kelimesini en iyi şekilde sevdim, bundan sonra bu kelimeyi kullanacağım.

Bilgisayar adını bulma

  • Linux / Unix'te bilgisayar adı, C işlevinden gethostname()veya Bash benzeri kabuklardaki hostnamekabuk veya HOSTNAMEortam değişkeninden komut alır .

  • Windows'ta bilgisayar adı, ortam değişkeni COMPUTERNAMEveya Win32 GetComputerNameişlevinden elde ettiğiniz addır .

Java'nın 'bilgisayaradı' olarak tanımladığım şeyi elde etmenin bir yolu yoktur. Elbette, Windows çağrısı gibi diğer yanıtlarda açıklandığı gibi geçici çözümler vardır System.getenv("COMPUTERNAME"), ancak Unix / Linux'ta JNI / JNA veya 'ya başvurmadan iyi bir geçici çözüm yoktur Runtime.exec(). Bir JNI / JNA çözümüne aldırmazsanız , ölü basit ve kullanımı çok kolay olan gethostname4j var .

Biri Linux ve diğeri Solaris'ten olmak üzere iki örnekle devam edelim, bu da standart Java yöntemlerini kullanarak bilgisayar adını nasıl elde edemeyeceğinize kolayca nasıl ulaşabileceğinizi gösterir.

Linux örneği

Yükleme sırasında ana bilgisayarın 'chicago' olarak adlandırıldığı yeni oluşturulan bir sistemde, şimdi çekirdek ana makine adını değiştiriyoruz:

$ hostnamectl --static set-hostname dallas

Artık ana makine adı komutundan da anlaşılacağı üzere çekirdek ana makine adı 'dallas':

$ hostname
dallas

Ama hala var

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Bunda yanlış bir yapılandırma yok. Bu yalnızca ana bilgisayarın ağa bağlı adının (veya geri döngü arayüzünün adının) ana bilgisayarın bilgisayar adından farklı olduğu anlamına gelir.

Şimdi, çalıştırmayı deneyin InetAddress.getLocalHost().getHostName() ve java.net.UnknownHostException atar. Temelde sıkışıp kaldınız. Ne 'dallas' değerini ne de 'chicago' değerini almanın bir yolu yoktur.

Solaris örneği

Aşağıdaki örnek Solaris 11.3'e dayanmaktadır.

Ana bilgisayar, geri döngü adı <> nodename olacak şekilde kasıtlı olarak yapılandırılmıştır.

Başka bir deyişle:

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

ve / etc / hosts öğelerinin içeriği:

:1 chicago localhost
127.0.0.1 chicago localhost loghost

ve hostname komutunun sonucu:

$ hostname
dallas

Sadece Linux örnekte bir çağrı gibi InetAddress.getLocalHost().getHostName()başarısız olur

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

Tıpkı Linux örneği gibi, artık sıkışıp kaldınız. Ne 'dallas' değerini ne de 'chicago' değerini almanın bir yolu yoktur.

Bununla ne zaman gerçekten mücadele edeceksin?

Çoğu zaman InetAddress.getLocalHost().getHostName()bunun gerçekten bilgisayar adına eşit bir değer döndüreceğini göreceksiniz . Yani sorun yok (isim çözümünün ek yükü hariç).

Sorun genellikle bilgisayar adı ve geridöngü arabiriminin adı arasında bir fark olduğu PaaS ortamlarında ortaya çıkar. Örneğin, insanlar Amazon EC2'deki sorunları bildiriyor.

Hata / RFE raporları

Biraz arama şu RFE raporunu ortaya çıkarır: link1 , link2 . Bununla birlikte, bu rapordaki yorumlardan yola çıkarak, sorunun JDK ekibi tarafından büyük ölçüde yanlış anlaşıldığı görülüyor, bu yüzden ele alınması muhtemel değildir.

RFE'deki diğer programlama dilleriyle karşılaştırmayı seviyorum.


12

Ortam değişkenleri de yararlı bir yol sağlayabilir - COMPUTERNAMEWindows'ta,HOSTNAME çoğu modern Unix / Linux kabuğunda .

Bkz. Https://stackoverflow.com/a/17956000/768795

Bunları "ek" yöntemler olarak kullanıyorum InetAddress.getLocalHost().getHostName(), çünkü birkaç kişinin işaret ettiği gibi, bu işlev tüm ortamlarda çalışmaz.

Runtime.getRuntime().exec("hostname")başka bir olası ek. Bu aşamada hiç kullanmadım.

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 nedir?? (Ne demek istediğini biliyorum sadece bu amaç için harici bir kütüphane getirmek kötü karma olduğunu düşünüyorum .. ve bunun üzerine bile bahsetmiyorum bile)
peterh

23
Sürekli "boş" çekleri manuel olarak yazmak kötü bir karma. Pek çok proje bu tür kontrolleri manuel olarak (önerdiğiniz şekilde) ve tutarsız bir şekilde, sonuçta ortaya çıkan birçok hata ile yapar. Bir kütüphane kullanın.
Thomas W

15
Herhangi birinin bunu görmesi ve aslında kafasını karıştırması durumunda StringUtils, Apache commons-lang projesi tarafından sağlanır. Bunu veya bunun gibi bir şeyi kullanmanız şiddetle tavsiye edilir.
JBCP

Bir şekilde System.getenv("HOSTNAME")Java için Beanshell aracılığıyla Mac'te null çıktı, ancak PATHtamam çıkarıldı. echo $HOSTNAMEMac'te de yapabilirim , sadece System.getenv ("HOSTNAME") sorunu var gibi görünüyor. Garip.
David

6

Geçerli bilgisayarın ana bilgisayar adını Java'da almanın en taşınabilir yolu aşağıdaki gibidir:

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
Taşınabilirlik bir yana, bu yöntem sorudaki yöntemle nasıl karşılaştırılır? Artıları / eksileri nelerdir?
Marquez

4

Eğer maven central'ın harici bir bağımlılığını kullanmaya karşı değilseniz, bu sorunu kendim çözmek için gethostname4j yazdım. Sadece libc'nin gethostname işlevini çağırmak için JNA kullanır (veya Windows'ta BilgisayarAdı alır) ve size bir dize olarak döndürür.

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
Bu yöntemin faydası nedir?
Sam Hasler

1
Bu geçersizdir, yalnızca geri döngü bağdaştırıcısı olmayan ilk NIC'nin adını sağlar.
Steve-o

aslında bir ana bilgisayar adı olan ilk geri döngü ... illa ki ilk geri döngü olmayan.
Adam Gent

1

Sadece bir astarlı ... çapraz platform (Windows-Linux-Unix-Mac (Unix)) [Her zaman çalışır, DNS gerekmez]:

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

Sen bittin !!


InetAddress.getLocalHost (). GetHostName () Unix'te çalışmaz mı?
P Satish Patro

1
Bu çalışır, ancak dns çözünürlük gerektirir, telefonunuzdan bir wifi tethering bağlıysanız (sadece bir örnek söz gerekirse) localhost makinesini bilen bir DNS olmaz ve işe yaramaz.
Dan Ortega

-2

InetAddress.getLocalHost (). GetHostName (), geliştirici düzeyindeki en iyi soyutlama olduğundan ikisinin en iyi yoludur.


Birçok hostname tarafından bilinen bir ev sahibi ile ilgilenen bir cevap arıyorum.
Sam Hasler

@ SamHasler, sizin özel durumunuz nedir? Arnout'un açıkladığı gibi, neye ihtiyacınız olduğuna bağlı olarak farklı yollar vardır.
Nick Knowlson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.