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á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âu trả lời:
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:
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
.
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 hostname
lệ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.
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.
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
Đọc COMPUTERNAME
biến môi trường thông qua System.getenv()
.
Thực hiện hostname.exe
và đọc phản hồi
Trên Linux
Đọc HOSTNAME
biến môi trường thông quaSystem.getenv()
Thực hiện hostname
và đọc phản hồi
Đọc /etc/hostname
(để thực hiện điều này Tôi đang thực thi cat
vì đ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 $HOSTNAME
trả 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 HOSTNAME
trướ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.
export HOSTNAME
trước khi chạy mã Java trên Ubuntu 14.04 LTS thì nó cũng được trả về System.getenv("HOSTNAME")
.
export HOSTNAME
cho Java để sử dụng nó? Tôi nghĩ rằng nó nên là một var env mặc định như PATH.
\A
dấ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
.
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ó.
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ờ.
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 hostname
lệnh từ shell hoặc HOSTNAME
biế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 COMPUTERNAME
hoặc GetComputerName
hà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.
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ụ 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'.
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.
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.
Các biến môi trường cũng có thể cung cấp một phương tiện hữu ích - COMPUTERNAME
trên Windows, HOSTNAME
trê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;
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.
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 $HOSTNAME
trên Mac, chỉ System.getenv ("HOSTNAME") dường như có vấn đề. Lạ thật.
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);
}
}
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.
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();
}
}
}
}
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 () 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.