java InetAddress.getLocalHost() возвращает 127.0.0.1 ... как получить РЕАЛЬНЫЙ IP?
Я разрабатываю простое сетевое приложение и мне нужно узнать настоящий IP-адрес моего устройства в сети, например, 192.168.1.3. Метод getLocalHost
возвращает 127.0.0.1 (на Linux, не уверен, будет ли это так же и на Windows). Как это сделать?
5 ответ(ов)
Если вы хотите работать со всеми IP-адресами на машине, вы можете получить их с помощью класса NetworkInterface
. Однако вам потребуется выбрать, какой именно адрес вы хотите использовать, так как это будет зависеть от ваших конкретных задач. Возможно, вам также придется изменить способ, которым вы используете адреса, чтобы учесть наличие нескольких IP-адресов.
Вот пример кода на Java, который демонстрирует, как получить все сетевые интерфейсы и соответствующие им адреса:
import java.net.*;
import java.util.*;
public class ShowInterfaces
{
public static void main(String[] args) throws Exception
{
System.out.println("Host addr: " + InetAddress.getLocalHost().getHostAddress()); // часто возвращает "127.0.0.1"
Enumeration<NetworkInterface> n = NetworkInterface.getNetworkInterfaces();
for (; n.hasMoreElements();)
{
NetworkInterface e = n.nextElement();
System.out.println("Interface: " + e.getName());
Enumeration<InetAddress> a = e.getInetAddresses();
for (; a.hasMoreElements();)
{
InetAddress addr = a.nextElement();
System.out.println(" " + addr.getHostAddress());
}
}
}
}
В этом коде мы сначала выводим локальный адрес хоста, который часто бывает "127.0.0.1". Затем мы получаем все сетевые интерфейсы, перебираем их и для каждого интерфейса выводим его имена и все связанные с ним IP-адреса. Это предоставляет вам возможность получить доступ ко всем сетевым интерфейсам и их адресам на вашей машине.
Когда у машины может быть несколько адресов, трудно определить, какой из них вам нужен. Обычно вы хотите, чтобы система сама назначила IP-адрес на основе таблицы маршрутизации. Поскольку результат зависит от IP-адреса, к которому вы хотите подключиться, существует простой трюк: просто создайте соединение и посмотрите, какой адрес вы получите от операционной системы:
// вывод на моей машине: "192.168.1.102"
Socket s = new Socket("192.168.1.1", 80);
System.out.println(s.getLocalAddress().getHostAddress());
s.close();
// вывод на моей машине: "127.0.1.1"
System.out.println(InetAddress.getLocalHost().getHostAddress());
Не уверен, возможно ли сделать это без установления соединения. Кажется, однажды мне удалось это сделать на Perl (или C?), но про Java не могу сказать. Возможно, можно создать UDP-сокет (DatagramSocket) без фактического подключения.
Однако, если на пути есть NAT-роутер, вы не сможете получить IP, который увидят удаленные хосты. Тем не менее, учитывая, что вы привели 192.* в качестве примера, думаю, вам это не так важно.
Чтобы это исправить:
Найдите ваше имя хоста. Введите команду:
hostname
. Например, вы можете узнать, что ваше имя хоста —mycomputer.xzy.com
.Добавьте ваше имя хоста в файл hosts. Откройте файл
/etc/hosts
и добавьте строчку:10.50.16.136 mycomputer.xzy.com
Вот способ избежать получения результатов IPv6 и адресов обратной связи (Loopback):
public InetAddress getCurrentIp() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface ni = networkInterfaces.nextElement();
Enumeration<InetAddress> nias = ni.getInetAddresses();
while (nias.hasMoreElements()) {
InetAddress ia = nias.nextElement();
if (!ia.isLinkLocalAddress()
&& !ia.isLoopbackAddress()
&& ia instanceof Inet4Address) {
return ia;
}
}
}
} catch (SocketException e) {
LOG.error("Не удалось получить текущий IP " + e.getMessage(), e);
}
return null;
}
В этом методе происходит перебор всех сетевых интерфейсов и их IP-адресов. Мы проверяем, чтобы адрес не был локальным (Link-Local) или адресом обратной связи, и дополнительно фильтруем только IPv4 адреса. Если найден подходящий адрес, он возвращается, иначе, в случае ошибки, выводится сообщение в лог. Этот подход позволяет получить нужный результат без нежелательных исключений.
Ваш код выглядит неплохо, но давайте рассмотрим его более подробно и проверим несколько моментов.
Общая структура кода
- Вы импортируете необходимые классы.
- Используете
Set<String>
для хранения уникальных IP-адресов хоста. - Перебираете все сетевые интерфейсы и проверяете их состояния.
Проверки
Вы правильно проверяете, что интерфейс не является циклическим (isLoopback()
), что он активен (isUp()
), и что у него есть MAC-адрес (т.е. getHardwareAddress() != null
). Эти проверки являются хорошими основанием для фильтрации интерфейсов.
Ваша логика
Однако стоит учитывать некоторые моменты:
- Если у вас несколько IP-адресов, относящихся к множеству интерфейсов, возможно, вам следует уточнить, какой именно протокол вы используете (IPv4 или IPv6), поскольку в вашем коде проверяется наличие широковещательного адреса, что обычно относится к IPv4.
- Также будет полезно обрабатывать исключения (например,
SocketException
) более информативно, возможно, выводя сообщение об ошибке, чтобы понимать, что пошло не так.
Рекомендации по улучшению
Вот некоторые рекомендации для улучшения вашего кода:
- Добавьте обработку ошибок, чтобы легче отслеживать исключения.
- Если вам нужны только IPv4-адреса, убедитесь, что вы фильтруете по этому критерию.
- Рассмотрите возможность использования более понятных имен переменных (например,
hostAddresses
вместоHostAddresses
).
Пример улучшенного кода
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
private String[] getHostAddresses() {
Set<String> hostAddresses = new HashSet<>();
try {
for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
if (!ni.isLoopback() && ni.isUp() && ni.getHardwareAddress() != null) {
for (InterfaceAddress ia : ni.getInterfaceAddresses()) {
if (ia.getBroadcast() != null) { // Фильтрация по IPv4
hostAddresses.add(ia.getAddress().getHostAddress());
}
}
}
}
} catch (SocketException e) {
e.printStackTrace(); // Логируем исключение для отладки
}
return hostAddresses.toArray(new String[0]);
}
Таким образом, Ваш код становится более надежным и легче отлаживаемым. Надеюсь, это поможет!
Инициализация ArrayList в одну строчку
Что значит 'synchronized'?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Как объявить массив в одну строку?
Какие проблемы следует учитывать при переопределении equals и hashCode в Java?