Android设备获取出口ip

常规获取ip方法

import java.net.*;
import java.util.List;
import java.util.logging.Logger;

public class NetworkIpUtils {
    // 示例:指定要查询的网络接口名称(如 "eth0"、"wlan0",需根据实际环境修改)
    private static final String INTERFACE_NAME = "wlan0";

    public static String getIpv4() {
        try {
            NetworkInterface ni = getInterface(INTERFACE_NAME);
            if (ni != null) {
                List<InterfaceAddress> addresses = ni.getInterfaceAddresses();
                for (InterfaceAddress add : addresses) {
                    InetAddress iad = add.getAddress();
                    // 过滤 IPv4 且非回环地址
                    if (iad instanceof Inet4Address && !iad.isLoopbackAddress()) {
                        log.info("ip4: " + iad.getHostAddress());
                    }
                }
            } else {
                log.warning("未找到名称为 " + INTERFACE_NAME + " 的网络接口");
            }
        } catch (SocketException e) {
            log.severe("获取网络接口信息失败:" + e.getMessage());
        }
    }

    // 补充实现 getInterface 方法:根据名称获取 NetworkInterface
    private static NetworkInterface getInterface(String interfaceName) throws SocketException {
        return NetworkInterface.getByName(interfaceName);
    }
}

实际结果是,可能有多个ip4: xx打印,也就是说我们在同一张网卡上有多个ip

网卡能有多个 IP 的常见原因

几个最常见的场景,解释为什么一个网卡会有多个 IP:

  1. 同时存在 IPv4 和 IPv6 地址(最常见)
    现代操作系统默认会给网卡同时分配IPv4 地址和IPv6 地址,你的代码里通过iad instanceof Inet4Address过滤了 IPv6,但如果不过滤的话,会看到明显的两个 IP(比如 192.168.1.100 和 fe80::xxxx:xxxx:xxxx:xxxx)。
    你的代码只打印 IPv4,所以如果网卡只有一个 IPv4 + 一个 IPv6,最终只输出 1 个 IPv4,看起来是 “1 个 IP”;
    但如果代码去掉Inet4Address过滤,就能看到多个 IP 了。
  2. 手动配置的 “IP 别名”(多 IP 绑定)
    在服务器 / 开发机场景中,运维或开发者会给网卡绑定多个 IPv4 别名,比如:
    主 IP:192.168.1.100(用于日常通信);
    别名 IP:192.168.1.101(用于特定服务 / 测试)。
    这种配置下,你的代码会直接读取到多个 IPv4 地址。
  3. 临时 / 辅助 IP(系统自动分配)
    链路本地地址:IPv4 有一个特殊的 “链路本地地址”(169.254.x.x),当网卡无法获取 DHCP 地址时,系统会自动分配这个 IP,导致网卡同时有 “正常 IP + 链路本地 IP”;
    VPN / 虚拟网卡叠加:如果开启了 VPN、容器网络(Docker)、虚拟机网络,物理网卡会关联虚拟 IP,也会出现多 IP。

但是实际与其它端通信的IP应该只有一个,那么究竟如何获取到这个“出口ip”呢

进阶获取ip方法

    // 核心方法:获取出口IP
    private static String getExitIp() {
        // 选择一个公网IP(无需连通,仅用于获取本地出口),比如阿里云公共DNS、谷歌DNS
        String testHost = "223.5.5.5"; // 阿里云DNS(推荐),也可以用 8.8.8.8(谷歌DNS)
        int testPort = 53; // DNS端口,几乎不会被屏蔽

        try (Socket socket = new Socket()) {
            // 连接到公网地址(仅建立连接,不发送数据)
            SocketAddress remoteAddr = new InetSocketAddress(testHost, testPort);
            socket.connect(remoteAddr, 3000); // 超时3秒

            // 获取本地绑定的IP(即出口IP)
            InetAddress localAddr = ((InetSocketAddress) socket.getLocalSocketAddress()).getAddress();
            return localAddr.getHostAddress();
        } catch (Exception e) {
            log.severe("获取出口IP异常:" + e.getMessage());
            return null;
        }
    }

核心思路

获取出口 IP 的关键不是直接读取本地网卡 IP,而是通过主动发起一个对外的网络连接(不需要实际发送数据),从连接的本地端点中获取真正用于通信的 IP。因为本地可能有多个网卡、多个 IP,但操作系统会根据路由表选择最优的出口 IP,这个方式能精准拿到这个IP

关键逻辑解释

  1. 为什么要连接公网地址?
    本地网卡的 IP 只是 "备选",操作系统会根据路由表决定用哪个 IP 访问公网。通过连接一个公网 IP(比如阿里云 DNS 223.5.5.5),操作系统会自动选择最优的出口 IP 绑定到这个 socket,我们只需读取这个本地绑定的 IP 即可。

  2. 测试地址选择原则:
    选公网、高可用的地址(如公共 DNS),避免选可能不通的地址;
    端口选通用端口(53 是 DNS 端口,80 是 HTTP 端口),几乎不会被防火墙屏蔽;
    代码中用 try-with-resources 自动关闭 socket,避免资源泄漏。

  3. 特殊情况处理:
    如果是内网环境(比如公司局域网),获取到的 "出口 IP" 是内网出口(如 192.168.1.100),而非公网 IP;
    如果要获取公网 IP(如 112.xx.xx.xx),需要调用公网 API(比如 http://icanhazip.com),补充代码如下:

// 补充:获取公网IP(需联网)
private static String getPublicIp() {
    try {
        // 调用公网API获取公网IP
        URL url = new URL("http://icanhazip.com");
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
            return reader.readLine().trim();
        }
    } catch (Exception e) {
        log.severe("获取公网IP异常:" + e.getMessage());
        return null;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容