我正在尝试开发一个系统,其中有不同的节点运行在不同的系统或在同一系统的不同端口上。
现在,所有节点都创建了一个套接字,将目标IP作为称为引导节点的特殊节点的IP。然后,节点创建自己的ServerSocket并开始侦听连接。
引导节点维护一个节点列表,并在查询时返回它们。
现在我需要的是节点必须将其IP注册到引导节点。我尝试使用cli.getInetAddress()一旦客户端连接到引导节点的ServerSocket,但这不起作用。
我需要客户端注册其PPP IP,如果可用;
否则,局域网IP(如果可用);
否则,它必须注册127.0.0.1,假设它是同一台计算机。
使用代码:
System.out.println(Inet4Address.getLocalHost().getHostAddress());
or
System.out.println(InetAddress.getLocalHost().getHostAddress());
我的PPP连接IP地址是:117.204.44.192,但上面返回我192.168.1.2
EDIT
我正在使用以下代码:
Enumeration e = NetworkInterface.getNetworkInterfaces();
while(e.hasMoreElements())
{
NetworkInterface n = (NetworkInterface) e.nextElement();
Enumeration ee = n.getInetAddresses();
while (ee.hasMoreElements())
{
InetAddress i = (InetAddress) ee.nextElement();
System.out.println(i.getHostAddress());
}
}
我能够获得所有网络接口相关的所有IP地址,但我如何区分它们?这是我得到的输出:
127.0.0.1
192.168.1.2
192.168.56.1
117.204.44.19
在这里发布来自https://issues.apache.org/jira/browse/JCS-40的测试IP歧义变通代码(InetAddress.getLocalHost()在Linux系统上是歧义的):
/**
* Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's LAN IP address.
* <p/>
* This method is intended for use as a replacement of JDK method <code>InetAddress.getLocalHost</code>, because
* that method is ambiguous on Linux systems. Linux systems enumerate the loopback network interface the same
* way as regular LAN network interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not
* specify the algorithm used to select the address returned under such circumstances, and will often return the
* loopback address, which is not valid for network communication. Details
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>.
* <p/>
* This method will scan all IP addresses on all network interfaces on the host machine to determine the IP address
* most likely to be the machine's LAN address. If the machine has multiple IP addresses, this method will prefer
* a site-local IP address (e.g. 192.168.x.x or 10.10.x.x, usually IPv4) if the machine has one (and will return the
* first site-local address if the machine has more than one), but if the machine does not hold a site-local
* address, this method will return simply the first non-loopback address found (IPv4 or IPv6).
* <p/>
* If this method cannot find a non-loopback address using this selection algorithm, it will fall back to
* calling and returning the result of JDK method <code>InetAddress.getLocalHost</code>.
* <p/>
*
* @throws UnknownHostException If the LAN address of the machine cannot be found.
*/
private static InetAddress getLocalHostLANAddress() throws UnknownHostException {
try {
InetAddress candidateAddress = null;
// Iterate all NICs (network interface cards)...
for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
// Iterate all IP addresses assigned to each card...
for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// Found non-loopback site-local address. Return it immediately...
return inetAddr;
}
else if (candidateAddress == null) {
// Found non-loopback address, but not necessarily site-local.
// Store it as a candidate to be returned if site-local address is not subsequently found...
candidateAddress = inetAddr;
// Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
// only the first. For subsequent iterations, candidate will be non-null.
}
}
}
}
if (candidateAddress != null) {
// We did not find a site-local address, but we found some other non-loopback address.
// Server might have a non-site-local address assigned to its NIC (or it might be running
// IPv6 which deprecates the "site-local" concept).
// Return this non-loopback candidate address...
return candidateAddress;
}
// At this point, we did not find a non-loopback address.
// Fall back to returning whatever InetAddress.getLocalHost() returns...
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
}
return jdkSuppliedAddress;
}
catch (Exception e) {
UnknownHostException unknownHostException = new UnknownHostException("Failed to determine LAN address: " + e);
unknownHostException.initCause(e);
throw unknownHostException;
}
}
你的计算机可以有多个网络接口,每个网络接口有多个inetaddress。如果过滤掉任何本地地址,则提醒的地址是非本地地址,其中可以有一个、没有或多个。
不幸的是,Java中的网络API仍然使用(旧的)枚举而不是迭代器和流,我们可以通过将它们包装为流来进行对抗。所以我们要做的就是
所有网络接口及其地址的流,以及
过滤掉本地的
代码:
private Stream<InetAddress> getNonLocalIpAddresses() throws IOException {
return enumerationAsStream(NetworkInterface.getNetworkInterfaces())
.flatMap(networkInterface -> enumerationAsStream(networkInterface.getInetAddresses()))
.filter(inetAddress -> !inetAddress.isAnyLocalAddress())
.filter(inetAddress -> !inetAddress.isSiteLocalAddress())
.filter(inetAddress -> !inetAddress.isLoopbackAddress())
.filter(inetAddress -> !inetAddress.isLinkLocalAddress());
}
在我的机器上,这目前返回两个IPv6地址。
要获得这些inetaddress中的第一个:
private String getMyIp() throws IOException {
return getNonLocalIpAddresses()
.map(InetAddress::getHostAddress)
.findFirst()
.orElseThrow(NoSuchElementException::new);
}
将枚举包装为流的方法:
public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
new Iterator<>() {
public T next() { return e.nextElement(); }
public boolean hasNext() { return e.hasMoreElements(); }
}, Spliterator.ORDERED), false);
}