Cách được đề xuất để có được tên máy chủ trong Java


274

Cách nào sau đây là cách tốt nhất và dễ mang theo nhất để có được tên máy chủ của máy tính hiện tại trong Java?

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

đấu với

InetAddress.getLocalHost().getHostName()


Công nghệ này là gì?
tom redfern

Tôi nghĩ rằng tên được hỗ trợ uname (uts_name) thực sự duy nhất là từ RMI / JMX VMID, nhưng đây là cách triển khai cụ thể.
eckes

Câu trả lời:


336

Nói đúng ra - bạn không có lựa chọn nào khác ngoài việc gọi một trong hai hostname(1)- trên Unix gethostname(2). Đây là tên của máy tính của bạn. Mọi nỗ lực xác định tên máy chủ bằng một địa chỉ IP như thế này

InetAddress.getLocalHost().getHostName()

chắc chắn sẽ thất bại trong một số trường hợp:

  • Địa chỉ IP có thể không phân giải thành bất kỳ tên nào. Thiết lập DNS xấu, thiết lập hệ thống xấu hoặc thiết lập nhà cung cấp xấu có thể là lý do cho việc này.
  • Một tên trong DNS có thể có nhiều bí danh gọi là CNAME. Chúng chỉ có thể được giải quyết theo một hướng chính xác: tên đến địa chỉ. Hướng ngược lại là mơ hồ. Cái nào là tên "chính thức"?
  • Một máy chủ có thể có nhiều địa chỉ IP khác nhau - và mỗi địa chỉ có thể có nhiều tên khác nhau. Hai trường hợp phổ biến là: Một cổng ethernet có một số địa chỉ IP "hợp lý" hoặc máy tính có một số cổng ethernet. Nó có thể được cấu hình cho dù họ chia sẻ một IP hoặc có các IP khác nhau. Điều này được gọi là "đa biến".
  • Một Tên trong DNS có thể phân giải thành một số Địa chỉ IP. Và không phải tất cả các địa chỉ đó phải được đặt trên cùng một máy tính! (Usecase: Một hình thức cân bằng tải đơn giản)
  • Chúng ta thậm chí không bắt đầu nói về địa chỉ IP động.

Cũng đừng nhầm lẫn tên của một địa chỉ IP với tên của máy chủ lưu trữ (tên máy chủ). Một phép ẩn dụ có thể làm cho nó rõ ràng hơn:

Có một thành phố lớn (máy chủ) được gọi là "London". Bên trong bức tường thành phố nhiều việc xảy ra. Thành phố có một số cổng (địa chỉ IP). Mỗi cổng có một tên ("Cổng phía Bắc", "Cổng sông", "Cổng Southampton" ...) nhưng tên của cổng không phải là tên của thành phố. Ngoài ra, bạn không thể suy ra tên của thành phố bằng cách sử dụng tên của một cổng - "Cổng phía Bắc" sẽ bắt được một nửa các thành phố lớn hơn chứ không chỉ một thành phố. Tuy nhiên - một người lạ (gói IP) đi dọc bờ sông và hỏi một người địa phương: "Tôi có một địa chỉ lạ: 'Rivergate, thứ hai bên trái, ngôi nhà thứ ba'. Bạn có thể giúp tôi không?" Người địa phương nói: "Tất nhiên, bạn đang đi đúng đường, chỉ cần đi tiếp và bạn sẽ đến đích trong vòng nửa giờ."

Điều này minh họa nó khá nhiều tôi nghĩ.

Tin tốt là: Tên máy chủ thực sự thường không cần thiết. Trong hầu hết các trường hợp, bất kỳ tên nào phân giải thành địa chỉ IP trên máy chủ này đều được. (Người lạ có thể vào thành phố bằng Northgate, nhưng người dân địa phương hữu ích dịch phần "thứ 2 bên trái".)

Nếu các trường hợp góc còn lại bạn phải sử dụng dứt khoát nguồn của thiết lập cấu hình này - đó là chức năng C gethostname(2). Chức năng đó cũng được gọi bởi chương trình hostname.


9
Không hoàn toàn là câu trả lời tôi đã hy vọng nhưng bây giờ tôi biết làm thế nào để hỏi một câu hỏi tốt hơn, cảm ơn.
Sam Hasler


4
Đó là một bài viết hay về những hạn chế mà bất kỳ phần mềm nào (không chỉ là chương trình Java) có trong việc xác định tên của máy chủ lưu trữ trên thế giới. Tuy nhiên, lưu ý rằng gethostName được triển khai theo các hệ điều hành cơ bản, có lẽ giống như tên máy chủ / gethostname. Trên hệ thống "bình thường", InetAddress.getLocalhost (). GethostName () tương đương với việc gọi tên máy chủ / gethostname, vì vậy bạn thực sự không thể nói rằng người khác thất bại theo cách khác.
Peter Cardona

System.err.println (Runtime.getR nb (). Exec ("tên máy chủ")); đưa cho tôi cái này: java.lang.UNIXProcess@6eb2384f
user52468

5
Việc triển khai InetAddress.getLocalhost (). GethostName () thực sự rất mang tính quyết định :) Nếu bạn theo mã nguồn, cuối cùng nó sẽ gọi gethostname () trên Windows và getaddrinfo () trên các hệ thống Unixy. Kết quả giống như sử dụng lệnh tên máy chủ hệ điều hành của bạn. Bây giờ tên máy chủ có thể cung cấp câu trả lời bạn không muốn sử dụng, điều đó có thể vì nhiều lý do. Nói chung, phần mềm sẽ lấy tên máy chủ từ người dùng trong tệp cấu hình, theo cách đó, nó luôn luôn là tên máy chủ chính xác. Bạn có thể sử dụng InetAddress.getLocalhost (). GethostName () làm mặc định nếu người dùng không cung cấp giá trị.
Greg

93

InetAddress.getLocalHost().getHostName() là cách di động hơn.

exec("hostname")thực sự gọi ra hệ điều hành để thực thi hostnamelệnh.

Dưới đây là một vài câu trả lời liên quan khác về SO:

EDIT: Bạn nên xem câu trả lời của AH hoặc câu trả lời của Arnout Engelen để biết chi tiết về lý do tại sao điều này có thể không hoạt động như mong đợi, tùy thuộc vào tình huống của bạn. Như một câu trả lời cho người này đặc biệt yêu cầu xách tay, tôi vẫn nghĩ getHostName()là ổn, nhưng họ đưa ra một số điểm tốt cần được xem xét.


12
Chạy gethostName () dẫn đến một số lỗi. Ví dụ, đây là những gì tôi nhận được trong một thể hiện Amazon EC2 AMI Linux: java.net.UnknownHostException: Tên hoặc dịch vụ không được biết java.net.InetAddress.getLocalHost (InetAddress.java:1438)
Marquez

1
Ngoài ra, InetAddress.getLocalHost()trong một số trường hợp trả về thiết bị loopback. Trong trường hợp đó, getHostName()trả về "localhost", không hữu ích lắm.
olenz

53

Như những người khác đã lưu ý, việc lấy tên máy chủ dựa trên độ phân giải DNS là không đáng tin cậy.

Vì câu hỏi này không may vẫn còn có liên quan trong năm 2018 , tôi muốn chia sẻ với bạn giải pháp độc lập với mạng của tôi, với một số thử nghiệm chạy trên các hệ thống khác nhau.

Các mã sau đây cố gắng làm như sau:

  • Trên Windows

    1. Đọc COMPUTERNAMEbiến môi trường thông qua System.getenv().

    2. Thực hiện hostname.exevà đọc phản hồi

  • Trên Linux

    1. Đọc HOSTNAMEbiến môi trường thông quaSystem.getenv()

    2. Thực hiện hostnamevà đọc phản hồi

    3. Đọc /etc/hostname(để thực hiện điều này Tôi đang thực thi catvì đoạn mã đã chứa mã để thực thi và đọc. Tuy nhiên, chỉ cần đọc tệp sẽ tốt hơn).

Mật mã:

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

Kết quả cho các hệ điều hành khác nhau:

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 Cái này hơi lạ vì echo $HOSTNAMEtrả về tên máy chủ chính xác, nhưng System.getenv("HOSTNAME")không:

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

EDIT: Theo legolas108 , System.getenv("HOSTNAME")hoạt động trên Ubuntu 14.04 nếu bạn chạy export HOSTNAMEtrước khi thực thi mã 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
"

Tên máy đã được thay thế nhưng tôi vẫn giữ nguyên chữ viết hoa và cấu trúc. Lưu ý thêm dòng mới khi thực hiện hostname, bạn có thể phải tính đến nó trong một số trường hợp.


3
Nếu bạn export HOSTNAMEtrước khi chạy mã Java trên Ubuntu 14.04 LTS thì nó cũng được trả về System.getenv("HOSTNAME").
legolas108

Bạn phải rõ ràng export HOSTNAMEcho Java để sử dụng nó? Tôi nghĩ rằng nó nên là một var env mặc định như PATH.
David

2
Vâng, đây là một trong những lỗi Java sẽ không bao giờ được sửa vì lý do tôn giáo. Một liên quan là có thể đặt tên quy trình. Lỗi đã đăng nhập gần 20 năm trước.
Tuntable

Câu trả lời tuyệt vời, rất kỹ lưỡng! Tôi không biết lý do tại sao bạn sử dụng dấu phân cách lạ đó, đặc biệt là xem xét mọi đầu ra được in có một dòng mới lạ trong đó. Bất kể, tôi đang cập nhật câu trả lời để hoạt động với MacOS, rút ​​ngắn indexOf để chứa các cuộc gọi và sửa tên biến hệ điều hành để phù hợp hơn với quy ước tên biến tiêu chuẩn.
Charlie

1
@Charlie \Adấu phân cách chỉ là một bản hack để đọc toàn bộ InputStream bằng cách sử dụng a Scanner.
Malt

24

InetAddress.getLocalHost().getHostName() là tốt hơn (như được giải thích bởi Nick), nhưng vẫn không tốt lắm

Một máy chủ có thể được biết đến dưới nhiều tên máy chủ khác nhau. Thông thường bạn sẽ tìm kiếm tên máy chủ lưu trữ của bạn trong một ngữ cảnh cụ thể.

Ví dụ: trong một ứng dụng web, bạn có thể đang tìm tên máy chủ được sử dụng bởi bất kỳ ai đưa ra yêu cầu mà bạn hiện đang xử lý. Làm thế nào để tìm ra cái tốt nhất phụ thuộc vào khung mà bạn đang sử dụng cho ứng dụng web của mình.

Trong một số loại dịch vụ trực tuyến khác, bạn sẽ muốn tên máy chủ dịch vụ của bạn có sẵn từ 'bên ngoài'. Do proxy, tường lửa, v.v ... đây thậm chí có thể không phải là tên máy chủ trên máy mà dịch vụ của bạn được cài đặt - bạn có thể thử đưa ra một mặc định hợp lý, nhưng bạn chắc chắn nên đặt cấu hình này cho bất kỳ ai cài đặt nó.


18

Mặc dù chủ đề này đã được trả lời có nhiều điều để nói.

Trước hết: Rõ ràng chúng ta cần một số định nghĩa ở đây. Cung InetAddress.getLocalHost().getHostName()cấp cho bạn tên của máy chủ lưu trữ khi nhìn từ góc độ mạng . Các vấn đề với cách tiếp cận này được ghi lại rõ ràng trong các câu trả lời khác: nó thường yêu cầu tra cứu DNS, thật mơ hồ nếu máy chủ có nhiều giao diện mạng và đôi khi nó chỉ bị lỗi (xem bên dưới).

Nhưng trên bất kỳ hệ điều hành nào cũng có một tên khác. Tên của máy chủ được xác định rất sớm trong quá trình khởi động, rất lâu trước khi mạng được khởi tạo. Windows dùng để chỉ này như computername , Linux gọi nó là hạt nhân hostname và Solaris sử dụng từ nodename . Tôi thích nhất tên máy tính từ , vì vậy tôi sẽ sử dụng từ đó từ bây giờ.

Tìm tên máy tính

  • Trên Linux / Unix, tên máy tính là những gì bạn nhận được từ hàm C gethostname()hoặc hostnamelệnh từ shell hoặc HOSTNAMEbiến môi trường trong các shell giống Bash.

  • Trên Windows, tên máy tính là những gì bạn nhận được từ biến môi trường COMPUTERNAMEhoặc GetComputerNamehàm Win32 .

Java không có cách nào để có được cái mà tôi đã định nghĩa là 'tên máy tính'. Chắc chắn, có những cách giải quyết như được mô tả trong các câu trả lời khác, như khi gọi Windows System.getenv("COMPUTERNAME"), nhưng trên Unix / Linux, không có cách giải quyết tốt nào mà không dùng đến JNI / JNA hay Runtime.exec(). Nếu bạn không quan tâm đến giải pháp JNI / JNA thì gethostname4j rất đơn giản và rất dễ sử dụng.

Hãy tiếp tục với hai ví dụ, một từ Linux và một từ Solaris, minh họa cách bạn có thể dễ dàng gặp tình huống khi bạn không thể có được tên máy tính bằng các phương thức Java tiêu chuẩn.

Ví dụ về Linux

Trên một hệ thống mới được tạo, nơi máy chủ trong quá trình cài đặt được đặt tên là 'chicago', giờ đây chúng ta thay đổi cái gọi là tên máy chủ kernel:

$ hostnamectl --static set-hostname dallas

Bây giờ tên máy chủ kernel là 'dallas', hiển nhiên từ lệnh tên máy chủ:

$ hostname
dallas

Nhưng chúng ta vẫn có

$ cat /etc/hosts
127.0.0.1   localhost
127.0.0.1   chicago

Không có cấu hình sai trong này. Nó chỉ có nghĩa là tên được nối mạng của máy chủ (hay đúng hơn là tên của giao diện loopback) khác với tên máy tính của máy chủ.

Bây giờ, hãy thử thực thi InetAddress.getLocalHost().getHostName() và nó sẽ ném java.net.Un UnknownhostException. Bạn đang bị mắc kẹt về cơ bản. Không có cách nào để lấy lại giá trị 'dallas' hay giá trị 'chicago'.

Ví dụ Solaris

Ví dụ dưới đây dựa trên Solaris 11.3.

Máy chủ đã cố tình được cấu hình sao cho tên loopback <> tên nút.

Nói cách khác, chúng ta có:

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

và nội dung của / etc / hosts:

:1 chicago localhost
127.0.0.1 chicago localhost loghost

và kết quả của lệnh hostname sẽ là:

$ hostname
dallas

Giống như trong ví dụ về Linux, một cuộc gọi đến InetAddress.getLocalHost().getHostName()sẽ thất bại với

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

Giống như ví dụ về Linux, bạn đang bị mắc kẹt. Không có cách nào để lấy lại giá trị 'dallas' hay giá trị 'chicago'.

Khi nào bạn sẽ thực sự đấu tranh với điều này?

Rất thường bạn sẽ thấy rằng InetAddress.getLocalHost().getHostName()thực sự sẽ trả về một giá trị bằng với tên máy tính. Vì vậy, không có vấn đề gì (ngoại trừ chi phí bổ sung cho độ phân giải tên).

Vấn đề phát sinh điển hình trong môi trường PaaS nơi có sự khác biệt giữa tên máy tính và tên của giao diện loopback. Ví dụ, mọi người báo cáo sự cố trong Amazon EC2.

Báo cáo lỗi / RFE

Một chút tìm kiếm cho thấy báo cáo RFE này: link1 , link2 . Tuy nhiên, việc đánh giá từ các bình luận về báo cáo đó về vấn đề này dường như đã bị nhóm JDK hiểu lầm, do đó không chắc sẽ được giải quyết.

Tôi thích sự so sánh trong RFE với các ngôn ngữ lập trình khác.


11

Các biến môi trường cũng có thể cung cấp một phương tiện hữu ích - COMPUTERNAMEtrên Windows, HOSTNAMEtrên hầu hết các hệ vỏ Unix / Linux hiện đại.

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

Tôi đang sử dụng những phương pháp này như là phương pháp "bổ sung" InetAddress.getLocalHost().getHostName(), vì như nhiều người chỉ ra, chức năng đó không hoạt động trong tất cả các môi trường.

Runtime.getRuntime().exec("hostname")là một bổ sung có thể. Ở giai đoạn này, tôi chưa sử dụng nó.

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? Đó là gì?? (Tôi biết ý của bạn là gì. Tôi chỉ nghĩ rằng đó là nghiệp xấu khi mang vào một thư viện bên ngoài cho mục đích duy nhất này .. và trên hết là không đề cập đến nó)
peterh

23
Nghiệp xấu khi liên tục viết séc "trống" bằng tay. Rất nhiều dự án có các kiểm tra như vậy được thực hiện thủ công (theo cách bạn đề xuất) và không nhất quán, với nhiều lỗi dẫn đến. Sử dụng một thư viện.
Thomas W

15
Trong trường hợp bất cứ ai nhìn thấy điều này và thực sự bối rối StringUtils, nó được cung cấp bởi dự án commons-lang của Apache. Sử dụng nó, hoặc một cái gì đó giống như nó, rất khuyến khích.
JBCP

Bằng cách nào đó System.getenv("HOSTNAME")đã xuất hiện null trên Mac thông qua Beanshell cho Java, nhưng PATHđược trích xuất ok. Tôi cũng có thể làm echo $HOSTNAMEtrên Mac, chỉ System.getenv ("HOSTNAME") dường như có vấn đề. Lạ thật.
David

6

Cách di động nhất để có được tên máy chủ của máy tính hiện tại trong Java như sau:

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
Tính di động sang một bên, làm thế nào để phương pháp này so với phương pháp trong câu hỏi? Ưu / nhược điểm là gì?
Marquez

4

Nếu bạn không chống lại việc sử dụng một phụ thuộc bên ngoài từ trung tâm maven, tôi đã viết gethostname4j để tự giải quyết vấn đề này. Nó chỉ sử dụng JNA để gọi hàm gethostname của libc (hoặc lấy Tên máy tính trên Windows) và trả lại cho bạn dưới dạng chuỗi.

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
Lợi ích của phương pháp này là gì?
Sam Hasler

1
Điều này không hợp lệ, nó chỉ cung cấp tên của NIC đầu tiên không phải là bộ điều hợp loopback.
Steve-o

Trên thực tế, đây không phải là loopback đầu tiên có tên máy chủ ... không nhất thiết phải là loopback đầu tiên.
Adam Gent

1

Chỉ một nền tảng ... đa nền tảng (Windows-Linux-Unix-Mac (Unix)) [Luôn hoạt động, Không cần DNS]:

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

Bạn đã hoàn tất !!


InetAddress.getLocalhost (). GethostName () sẽ không hoạt động trong Unix?
P Satish Patro

1
Điều đó hoạt động nhưng yêu cầu độ phân giải dns, nếu bạn được kết nối với kết nối wifi từ điện thoại của bạn (chỉ đề cập đến một ví dụ) thì nó sẽ không có DNS biết máy localhost và nó sẽ không hoạt động.
Dan Ortega

-2

InetAddress.getLocalhost (). GethostName () là cách tốt nhất trong số hai vì đây là cách trừu tượng tốt nhất ở cấp độ nhà phát triển.


Tôi đang tìm kiếm một câu trả lời liên quan đến một máy chủ lưu trữ được biết bởi nhiều tên máy chủ.
Sam Hasler

@SamHasler, tình hình cụ thể của bạn là gì? Như Arnout đã giải thích, có nhiều cách khác nhau tùy thuộc vào những gì bạn cần.
Nick knowlson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.