Unix/Linux 平台通过 IP 地址获取接口名的 C++ 实现

🌐 Unix/Linux 平台通过 IP 地址获取接口名的 C++ 实现


📌 函数声明
cpp 复制代码
ppp::string UnixAfx::GetInterfaceName(const IPEndPoint& address) noexcept {
  • 功能 :根据给定的 IP 端点(IPEndPoint)获取对应的网络接口名称(如 eth0)。
  • 返回值:成功返回接口名,失败返回空字符串。
  • 平台限制:Android 需 API ≥ 24(Android 7.0+)。

🔍 代码逐行解析(含中文注释)
cpp 复制代码
#if (!defined(_ANDROID) || __ANDROID_API__ >= 24)

🔹 平台兼容性检查

  • 仅在非 Android 或 Android API ≥ 24 时编译以下代码。

cpp 复制代码
    struct ifaddrs* ifa = NULL;
    if (getifaddrs(&ifa)) {
        return "";
    }

🔹 获取网络接口列表

  • getifaddrs 获取所有网络接口信息链表头 ifa
  • 失败时返回空字符串(如权限不足)。

cpp 复制代码
    struct ifaddrs* oifa = ifa;  // 保存链表头用于后续释放内存
    while (NULL != ifa) {
        struct sockaddr* addr = ifa->ifa_addr;
        if (NULL != addr) {

🔹 遍历接口链表

  • oifa 备份链表头指针,确保后续能正确释放内存。
  • 跳过无地址信息的接口(ifa_addrNULL)。

cpp 复制代码
            switch (addr->sa_family) {
                case AF_INET: {  // IPv4 地址
                    if (address.GetAddressFamily() != AddressFamily::InterNetwork) {
                        break;
                    }

🔹 处理 IPv4 地址

  • 检查目标地址是否为 IPv4 类型(InterNetwork)。
  • 类型不匹配则跳过当前接口。

cpp 复制代码
                    struct sockaddr_in* in4_addr = (struct sockaddr_in*)addr;
                    if (in4_addr->sin_addr.s_addr != address.GetAddress()) {
                        break;
                    }

🔹 比较 IPv4 地址

  • 将接口地址转为 sockaddr_in 结构。
  • 对比接口的 IPv4 地址(s_addr)与目标地址是否一致。

cpp 复制代码
                    freeifaddrs(oifa);  // 释放接口链表内存
                    return ifa->ifa_name;  // 返回匹配的接口名
                }

🔹 匹配成功处理

  • 释放链表内存,返回当前接口名称(如 "eth0")。

cpp 复制代码
                case AF_INET6: {  // IPv6 地址
                    if (address.GetAddressFamily() != AddressFamily::InterNetworkV6) {
                        break;
                    }

🔹 处理 IPv6 地址

  • 检查目标地址是否为 IPv6 类型(InterNetworkV6)。

cpp 复制代码
                    struct sockaddr_in6* in6_addr = (struct sockaddr_in6*)addr; {
                        int length;
                        Byte* address_bytes = address.GetAddressBytes(length);
                        length = std::min<int>(sizeof(in6_addr->sin6_addr), length);
                        if (memcmp(&in6_addr->sin6_addr, address_bytes, length) != 0) {
                            break;
                        }
                    }

🔹 比较 IPv6 地址

  • 获取目标地址的字节数组(address_bytes)。
  • 使用 memcmp 比较接口的 IPv6 地址(128 位)是否一致。

cpp 复制代码
                    freeifaddrs(oifa);
                    return ifa->ifa_name;  // 返回匹配的接口名
                }
            };
        }
        ifa = ifa->ifa_next;  // 移动到下一个接口节点
    }

🔹 继续遍历

  • 若当前接口不匹配,移动到链表下一个节点。

cpp 复制代码
    if (NULL != oifa) {
        freeifaddrs(oifa);  // 释放整个接口链表
    }
#endif
    return "";  // 未找到匹配接口
}

🔹 收尾处理

  • 遍历结束后释放接口链表内存。
  • 返回空字符串表示未找到匹配接口。

🧩 完整代码实现

cpp 复制代码
ppp::string UnixAfx::GetInterfaceName(const IPEndPoint& address) noexcept {
#if (!defined(_ANDROID) || __ANDROID_API__ >= 24)
    struct ifaddrs* ifa = NULL;
    // 获取所有网络接口信息链表
    if (getifaddrs(&ifa)) {
        return ""; // 获取失败返回空
    }

    struct ifaddrs* oifa = ifa; // 备份链表头指针
    while (NULL != ifa) {
        struct sockaddr* addr = ifa->ifa_addr;
        if (NULL != addr) {
            switch (addr->sa_family) {
                case AF_INET: { // IPv4 处理分支
                    // 检查地址族是否匹配
                    if (address.GetAddressFamily() != AddressFamily::InterNetwork) {
                        break;
                    }

                    struct sockaddr_in* in4_addr = (struct sockaddr_in*)addr;
                    // 比较 IPv4 地址是否一致
                    if (in4_addr->sin_addr.s_addr != address.GetAddress()) {
                        break;
                    }

                    freeifaddrs(oifa); // 释放链表内存
                    return ifa->ifa_name; // 返回接口名
                }
                case AF_INET6: { // IPv6 处理分支
                    if (address.GetAddressFamily() != AddressFamily::InterNetworkV6) {
                        break;
                    }

                    struct sockaddr_in6* in6_addr = (struct sockaddr_in6*)addr; {
                        int length;
                        // 获取目标地址的二进制形式
                        Byte* address_bytes = address.GetAddressBytes(length);
                        // 安全比较长度(IPv6 固定为 16 字节)
                        length = std::min<int>(sizeof(in6_addr->sin6_addr), length);
                        // 内存比较 IPv6 地址
                        if (memcmp(&in6_addr->sin6_addr, address_bytes, length) != 0) {
                            break;
                        }
                    }

                    freeifaddrs(oifa);
                    return ifa->ifa_name; // 返回接口名
                }
            };
        }
        ifa = ifa->ifa_next; // 遍历下一个接口
    }

    if (NULL != oifa) {
        freeifaddrs(oifa); // 释放整个链表
    }
#endif
    return ""; // 未找到匹配接口
}

💎 关键设计总结

  1. 跨平台兼容性
    • 通过 #if 确保 Android 低版本不编译此逻辑。
  2. 资源安全管理
    • 使用 oifa 备份链表头,确保任何退出路径都能正确释放内存。
  3. 双协议栈支持
    • 独立处理 IPv4/IPv6 地址,通过 memcmp 精确匹配 IPv6 地址。
  4. 高效遍历
    • 链表遍历在匹配成功后立即退出,避免无效搜索。

提示 :实际使用时需确保 IPEndPoint 类正确实现 GetAddress()(IPv4)和 GetAddressBytes()(IPv6)方法。

相关推荐
深耕AI3 小时前
【MFC 小白日记】对话框编辑器里“原型图像”到底要不要勾?3 分钟看懂!
c++·编辑器·mfc
Nerd Nirvana3 小时前
C++编程——异步处理、事件驱动编程和策略模式
开发语言·c++·策略模式·嵌入式开发·事件驱动·异步处理
一拳一个呆瓜3 小时前
【MFC】对话框节点属性:Condition(条件)
c++·mfc
阿贤Linux3 小时前
设置网卡名称为传统命名方式
linux·ubuntu
快去睡觉~4 小时前
力扣416:分割等和子集
数据结构·c++·算法·leetcode·职场和发展·动态规划
码界奇点4 小时前
从零构建Linux Shell解释器深入理解Bash进程创建机制
linux·运维·解释器模式·bash·ux·源代码管理
闻道且行之4 小时前
嵌入式|Linux中打开视频流的两种方式V4l2和opencv
linux·笔记·opencv·嵌入式
等风来不如迎风去4 小时前
【bash】命令查看当前目录下文件个数
linux·运维·服务器
2501_920047034 小时前
bash自带的切片操作
开发语言·python·bash