使用Java获取当前计算机的IP地址

344

我正在尝试开发一个系统,其中有不同的节点在不同的系统或同一系统的不同端口上运行。

现在,所有节点都会创建一个Socket,其中目标IP是称为引导节点的特殊节点的IP。然后,节点创建自己的ServerSocket并开始侦听连接。

引导节点维护节点列表,并在查询时返回它们。

现在我需要的是,节点必须向引导节点注册其IP。我尝试使用cli.getInetAddress()一旦客户端连接到引导节点的ServerSocket,但那行不通。

  1. 如果PPP IP可用,则需要客户端注册其PPP IP;
  2. 否则,如果可用,则需要注册LAN IP;
  3. 否则,假定注册127.0.0.1表示它与相同计算机。

使用的代码:

System.out.println(Inet4Address.getLocalHost().getHostAddress());
或者
System.out.println(InetAddress.getLocalHost().getHostAddress());

我的PPP连接IP地址是:117.204.44.192,但是上面的代码返回给我192.168.1.2。

编辑

我正在使用以下代码:

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

我能够获取所有与NetworkInterface相关联的IP地址,但如何区分它们?这是我得到的输出:

127.0.0.1
192.168.1.2
192.168.56.1
117.204.44.19

3
如果我在循环内部添加 n.isPointToPoint(),这样会起作用吗?我的想法是,如果没有找到点对点网络,则返回“127.0.0.1”。这样行得通吗? - sasidhar
3
请勿张贴您的真实IP地址。对于私人IP,请写117.xxx.xxx.xxx,这是可以接受的。 - nIcE cOw
@sattu 内部的 while 循环是为了获取绑定到此特定 NetworkInterface 的所有 InetAddresses。一个网络接口可能与一个或多个 InetAddresses 相关联。 - sasidhar
@sattu 在 Linux 发行版上尝试 ifconfig -a 命令。然后你就能看到什么是“网络接口”了。那个循环迭代这些接口。 - jilen
2
相关:https://dev59.com/l2025IYBdhLWcg3wVkif - AlikElzin-kilaka
显示剩余3条评论
19个回答

302

在最一般的情况下,这可能有点棘手。

表面上看,InetAddress.getLocalHost()应该为您提供此主机的IP地址。问题是,一个主机可能有很多网络接口,一个接口可能绑定到多个IP地址。而且更重要的是,并不是所有的IP地址都可以在您的计算机或局域网外访问。例如,它们可能是虚拟网络设备的IP地址、专用网络IP地址等。

这意味着InetAddress.getLocalHost()返回的IP地址可能不是正确的使用方法。

你该怎么办呢?

  • 一种方法是使用NetworkInterface.getNetworkInterfaces()获取主机上所有已知的网络接口,然后迭代每个NI的地址。
  • 另一种方法是(某种方式)获取主机的外部广告FQDN,并使用InetAddress.getByName()查找主IP地址。(但如何获取它,以及如何处理基于DNS的负载平衡器?)
  • 前面的变体是从配置文件或命令行参数中获取首选FQDN。
  • 另一种变体是从配置文件或命令行参数中获取首选IP地址。

总之,InetAddress.getLocalHost()通常有效,但您可能需要为在“复杂”网络环境中运行代码的情况提供替代方法。


我能够获取所有网络接口关联的所有IP地址,但如何区分它们?

  • 127.xxx.xxx.xxx范围内的任何地址都是“环回”地址。它只对“此”主机可见。
  • 192.168.xxx.xxx范围内的任何地址都是私有(又名站点本地)IP地址。这些地址被保留用于组织内部使用。10.xxx.xxx.xxx地址、172.16.xxx.xxx到172.31.xxx.xxx也同样适用。
  • 169.254.xxx.xxx地址范围内的IP地址是本地链接地址。这些地址仅保留在单个网络段中使用。
  • 224.xxx.xxx.xxx到239.xxx.xxx.xxx之间的地址属于多播地址。
  • 地址255.255.255.255是广播地址。
  • 其他任何地址应该是有效的公共点对点IPv4地址。
  • 实际上,InetAddress API提供了测试回环、本地链接、站点本地、多播和广播地址的方法。您可以使用这些方法来确定哪个IP地址最合适。


    6
    如果有人感到好奇,getLocalHost基本上对服务器主机名进行DNS查找。如果从该查找获得IP地址,则它会搜索可用的接口以查看哪个接口具有该IP地址,并返回该接口。这意味着在“服务器”环境中,getLocalHost通常能够正常工作,因为外发IP是映射到服务器主机名的IP地址。 - Pace
    1
    在Ubuntu 14.04上,即使ifconfig仅报告两个接口(我想要的一个是公共可访问的IP地址,另一个是回环127.0.0.1),此API返回127.0.1.1。奇怪的是它返回了不同的回环别名。 - ctpenrose
    我要补充一点,如果你使用 getLocalHost().getHostAddress() 来发布一些东西,当从网络上的另一台计算机查看时,你可能会看到 0.0.0.0。这在这里有解释。 当我在两台计算机之间使用Gazebo时,就发生了这种情况。 - Peter Mitrano

    175
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    
    try(final DatagramSocket socket = new DatagramSocket()){
      socket.connect(InetAddress.getByName("8.8.8.8"), 10002);
      ip = socket.getLocalAddress().getHostAddress();
    }
    

    当有多个网络接口时,这种方法效果良好。它总是返回首选的出站IP。目标8.8.8.8不需要可访问。

    UDP套接字上的连接具有以下效果:为发送/接收设置目标地址,丢弃来自其他地址的所有数据包,并将套接字转换为“已连接”状态,设置其适当的字段——这就是我们使用的功能。这包括根据系统的路由表检查到达目标的路由是否存在并相应地设置本地端点。最后一部分似乎没有正式记录,但它看起来像是Berkeley套接字API的一个重要特征(UDP“已连接”状态的副作用),可以在Windows和Linux的各个版本和发行版中可靠工作。

    因此,此方法将给出将用于连接到指定远程主机的本地地址。没有实际建立连接,因此指定的远程IP可能无法访问。

    编辑:

    @macomgil所说,对于MacOS,您可以执行以下操作:

    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("google.com", 80));
    System.out.println(socket.getLocalAddress());
    

    10
    在 Linux 上,这对我有用,但在 OsX 上我收到了:"0.0.0.0" 的错误信息。 - Radu Toader
    1
    太棒了!顺便提一下,当处理封闭的内部网络时,只需将8.8.8.8替换为每个主机都可以访问的地址。 - Murphy Ng
    适用于Windows;我们能确认一下OSX是否仍然存在问题吗? - trilogy
    “丢弃来自其他地址的所有数据包”这行代码的意思是仅接受特定地址的数据包,而忽略来自其他地址的数据包。 - omjego
    6
    我仍然在OSX上看到0.0.0.0。 - Peter Tutervai
    显示剩余5条评论

    61

    我在这里发布测试IP模糊解决方案代码,该代码来自https://issues.apache.org/jira/browse/JCS-40(在Linux系统上,InetAddress.getLocalHost()不明确):

    /**
     * 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;
        }
    }
    

    7
    必须注意的是,如果主机具有多个类似的网络接口,这仍然无法解决歧义问题。 - Vadzim
    2
    以下是更好的答案 - https://dev59.com/IGkx5IYBdhLWcg3wCP-Y#38342964 可以获取用作默认网关源的本地IP地址。 - Radu Toader
    为什么IP地址前面要加斜杠(/)?比如/10.39.0.17..?这样做总是必要的吗?需要修剪吗? - Kanagavelu Sugumar

    57
    你可以使用Java的InetAddress类来实现这个目的。
    InetAddress IP=InetAddress.getLocalHost();
    System.out.println("IP of my system is := "+IP.getHostAddress());
    

    我的系统输出为: IP地址为:10.100.98.228

    getHostAddress() 返回值为

    以文本形式返回 IP 地址字符串。

    或者你也可以这样做

    InetAddress IP=InetAddress.getLocalHost();
    System.out.println(IP.toString());
    

    输出 = 我的系统IP是 := RanRag-PC/10.100.98.228


    10
    请注意,10.x.x.x 是一个私有地址,表明你的系统在NAT网络中。当与外部世界联系时,它将显示为不同的地址。如果你真的需要外部IP地址,你需要联系许多可以回显你来自的IP地址的网站之一。这可能对你有用,也可能没有。无论如何,你的系统几乎肯定无法从外部访问到。 - Edward Falk
    哦,这救了我的命。以前我使用 clientSocket.getInetAddress().getHostName(); - NM Naufaldo
    2
    此解决方案在Linux系统上无法工作。 - Gaurang

    26

    当你在寻找“本地”地址时,需要注意每台机器不仅有一个网络接口,每个接口都可以有自己的本地地址。这意味着你的机器始终拥有多个“本地”地址。

    连接不同的终端时,将自动选择使用不同的“本地”地址。例如,当你连接到 google.com 时,你使用的是一个“外部”的本地地址;但当你连接到你的 localhost 时,你的本地地址总是 localhost 本身,因为 localhost 只是一种回环地址。

    以下展示了如何在与 google.com 通信时查找本地地址:

    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("google.com", 80));
    System.out.println(socket.getLocalAddress());
    socket.close();
    

    太棒了!!- 真的很简单 :) - smilyface
    6
    请在结尾处添加socket.close() :) - M.C.

    12

    在 Scala 中的示例(在 sbt 文件中很有用):

      import collection.JavaConverters._
      import java.net._
    
      def getIpAddress: String = {
    
        val enumeration = NetworkInterface.getNetworkInterfaces.asScala.toSeq
    
        val ipAddresses = enumeration.flatMap(p =>
          p.getInetAddresses.asScala.toSeq
        )
    
        val address = ipAddresses.find { address =>
          val host = address.getHostAddress
          host.contains(".") && !address.isLoopbackAddress
        }.getOrElse(InetAddress.getLocalHost)
    
        address.getHostAddress
      }
    

    10

    编辑1:由于之前链接不存在了,更新了代码

    import java.io.*;
    import java.net.*;
    
    public class GetMyIP {
        public static void main(String[] args) {
            URL url = null;
            BufferedReader in = null;
            String ipAddress = "";
            try {
                url = new URL("http://bot.whatismyipaddress.com");
                in = new BufferedReader(new InputStreamReader(url.openStream()));
                ipAddress = in.readLine().trim();
                /* IF not connected to internet, then
                 * the above code will return one empty
                 * String, we can check it's length and
                 * if length is not greater than zero, 
                 * then we can go for LAN IP or Local IP
                 * or PRIVATE IP
                 */
                if (!(ipAddress.length() > 0)) {
                    try {
                        InetAddress ip = InetAddress.getLocalHost();
                        System.out.println((ip.getHostAddress()).trim());
                        ipAddress = (ip.getHostAddress()).trim();
                    } catch(Exception exp) {
                        ipAddress = "ERROR";
                    }
                }
            } catch (Exception ex) {
                // This try will give the Private IP of the Host.
                try {
                    InetAddress ip = InetAddress.getLocalHost();
                    System.out.println((ip.getHostAddress()).trim());
                    ipAddress = (ip.getHostAddress()).trim();
                } catch(Exception exp) {
                    ipAddress = "ERROR";
                }
                //ex.printStackTrace();
            }
            System.out.println("IP Address: " + ipAddress);
        }
    }
    

    实际版本:这停止工作了

    希望这段代码片段能帮助你实现这个:

    // Method to get the IP Address of the Host.
    private String getIP()
    {
        // This try will give the Public IP Address of the Host.
        try
        {
            URL url = new URL("http://automation.whatismyip.com/n09230945.asp");
            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
            String ipAddress = new String();
            ipAddress = (in.readLine()).trim();
            /* IF not connected to internet, then
             * the above code will return one empty
             * String, we can check it's length and
             * if length is not greater than zero, 
             * then we can go for LAN IP or Local IP
             * or PRIVATE IP
             */
            if (!(ipAddress.length() > 0))
            {
                try
                {
                    InetAddress ip = InetAddress.getLocalHost();
                    System.out.println((ip.getHostAddress()).trim());
                    return ((ip.getHostAddress()).trim());
                }
                catch(Exception ex)
                {
                    return "ERROR";
                }
            }
            System.out.println("IP Address is : " + ipAddress);
    
            return (ipAddress);
        }
        catch(Exception e)
        {
            // This try will give the Private IP of the Host.
            try
            {
                InetAddress ip = InetAddress.getLocalHost();
                System.out.println((ip.getHostAddress()).trim());
                return ((ip.getHostAddress()).trim());
            }
            catch(Exception ex)
            {
                return "ERROR";
            }
        }
    }
    

    2
    这个解决方案可能会起作用,但前提是我必须始终连接到互联网,但这并不保证。此外,当系统未连接到互联网时,如果有的话,我需要返回系统的局域网IP地址,否则返回本地主机。所以对我来说不可行。还有其他方法吗? - sasidhar
    @sasidhar:当您连接到互联网时,才会有公共IP地址,如果没有连接,则此方法将给出本地IP地址或LAN IP地址。对于您指定的最后一种情况,您可以返回“127.0.0.1”,而不是返回错误。 - nIcE cOw
    1
    我喜欢你的想法,但那个链接似乎不再起作用了!我能否在自己的系统上放置一个控制器来代替那个外部链接,从而更可靠? - azerafati
    1
    @Bludream:非常感谢您让我知道链接已经失效。我已经更新了帖子,添加了一些新的内容。希望对您有用。关于您的问题,我真的不知道如何在您自己的系统上设置控制器以使其工作。因此,我无法就这个话题提供见解,我的错。再次感谢,保持微笑 :-) - nIcE cOw
    1
    尽管这是一个很酷的解决方案,但它非常不可靠。如果您阻塞了主线程(假设),并且由于某种原因whatismyip.com长时间无法访问,您的应用程序也将崩溃:(。或者它会返回垃圾数据并导致意外行为。此外,它返回的是whatismyip.com可检测到的最外层IP地址,而不一定是您正在使用的机器的IP地址。 - Decoded
    显示剩余3条评论

    9
    private static InetAddress getLocalAddress(){
            try {
                Enumeration<NetworkInterface> b = NetworkInterface.getNetworkInterfaces();
                while( b.hasMoreElements()){
                    for ( InterfaceAddress f : b.nextElement().getInterfaceAddresses())
                        if ( f.getAddress().isSiteLocalAddress())
                            return f.getAddress();
                }
            } catch (SocketException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    1
    请考虑为您的代码添加一些解释,说明其功能。 - hcarrasko

    6
    你可以使用 java.net.InetAddress API。 试试这个:
    InetAddress.getLocalHost().getHostAddress();
    

    6
    它只会返回 127.0.0.1。 - hcarrasko
    冗余回答:https://dev59.com/IGkx5IYBdhLWcg3wCP-Y#25257429说的是同样的事情。 - Stephen C

    6

    首先导入该类。

    import java.net.InetAddress;
    

    在类中

      InetAddress iAddress = InetAddress.getLocalHost();
      String currentIp = iAddress.getHostAddress();
      System.out.println("Current IP address : " +currentIp); //gives only host address
    

    2
    它只会给出第一个IP地址,即使它不是正在使用的IP地址! - Yahya

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接