ifaddrstruct在C语言中如何使用?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

ifaddrs 是什么?

ifaddrs 结构体定义在 <ifaddrs.h> 头文件中,它的主要作用是提供一个链表,链表中的每个节点都代表一个网络接口(eth0, wlan0, lo 等),并包含了该接口的详细地址信息(如 IP 地址、子网掩码、广播地址等)。

c语言ifaddrstruct
(图片来源网络,侵删)

当你需要编写一个程序来获取本机所有网络接口及其配置时,ifaddrs 是最标准、最常用的方法。

核心结构体定义

ifaddrs 结构体本身很简单,它包含两个主要部分:

  1. 一个指向下一个 ifaddrs 结构体的指针,用于构建链表。
  2. 一个名为 ifa_addr 的指针,它指向一个更通用的地址结构体(通常是 sockaddr 或其子类型,如 sockaddr_in for IPv4, sockaddr_in6 for IPv6)。

以下是 <ifaddrs.h> 中的典型定义:

#include <sys/socket.h> // 包含 sockaddr 的定义
#include <ifaddrs.h>
struct ifaddrs {
    struct ifaddrs  *ifa_next;    // 指向链表中的下一个接口
    char            *ifa_name;    // 接口名称,"eth0", "lo"
    unsigned int     ifa_flags;   // 接口标志位 (IFF_UP, IFF_LOOPBACK 等)
    struct sockaddr *ifa_addr;    // 接口的网络地址 (IPv4/IPv6)
    struct sockaddr *ifa_netmask; // 接口的子网掩码
    union {
        struct sockaddr *ifu_broadaddr; // 广播地址 (IPv4)
        struct sockaddr *ifu_dstaddr;   // 点对点目标地址 (IPv4)
    } ifa_ifu;
    #define ifa_broadaddr ifa_ifu.ifu_broadaddr
    #define ifa_dstaddr   ifa_ifu.ifu_dstaddr
    void            *ifa_data;    // 接口特定的额外数据 (不常用)
};

关键成员解析:

c语言ifaddrstruct
(图片来源网络,侵删)
  • ifa_next: 链表指针,遍历时使用。
  • ifa_name: 字符串,表示网络接口的名称。
  • ifa_flags: 整数,表示接口的状态,最常用的标志是:
    • IFF_UP (1): 接口已开启。
    • IFF_RUNNING (2): 接口已运行(网线已连接)。
    • IFF_LOOPBACK (8): 接口是回环接口 (lo)。
    • IFF_BROADCAST (16): 接口支持广播。
    • IFF_POINTOPOINT (32): 接口是点对点连接。
  • ifa_addr: 最重要的成员,它是一个指向 struct sockaddr 的通用指针,你不能直接使用它,需要根据地址族(ifa_addr->sa_family)将其转换为具体的类型:
    • ifa_addr->sa_family == AF_INET,则转换为 struct sockaddr_in *,用于处理 IPv4 地址。
    • ifa_addr->sa_family == AF_INET6,则转换为 struct sockaddr_in6 *,用于处理 IPv6 地址。
  • ifa_netmask: 子网掩码,同样是一个通用 sockaddr 指针,需要根据地址族进行转换。
  • ifa_broadaddr / ifa_dstaddr: 分别对应广播地址和点对点目标地址,只在特定类型的接口上存在。
  • ifa_data: 一个预留的指针,用于存储接口特定的额外数据,现代程序中很少使用。

如何使用 ifaddrs:标准流程

使用 ifaddrs 的标准流程分为三步:

  1. 获取链表: 调用 getifaddrs() 函数,它会返回一个指向链表头部的指针。
  2. 遍历链表: 使用一个循环,顺着 ifa_next 指针遍历整个链表。
  3. 释放内存: 调用 freeifaddrs() 函数,释放 getifaddrs() 分配的内存。这一步至关重要,否则会导致内存泄漏。

相关函数:

  • int getifaddrs(struct ifaddrs **ifap);
    • 功能:获取所有网络接口的信息,并将结果存储在一个链表中。
    • 参数 ifap 是一个二级指针,函数会在这里返回链表头部的地址。
    • 成功时返回 0,失败时返回 -1 并设置 errno
  • void freeifaddrs(struct ifaddrs *ifa);
    • 功能:释放 getifaddrs() 分配的整个链表的内存。
    • 参数 ifagetifaddrs() 返回的链表头指针。

完整代码示例

下面的示例代码会打印出所有已开启的网络接口的名称、IPv4 地址和子网掩码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <arpa/inet.h> // 用于 inet_ntoa
// 辅助函数:将 sockaddr 转换为可读的字符串 (IPv4)
void print_ipv4_info(struct sockaddr_in *addr) {
    char str[INET_ADDRSTRLEN];
    // inet_ntoa 是较旧的函数,inet_ntop 更现代,但这里为了简单使用 inet_ntoa
    // 注意: inet_ntoa 会覆盖内部静态缓冲区,在循环中多次调用可能有问题
    // 更好的做法是使用 inet_ntop
    if (inet_ntop(AF_INET, &(addr->sin_addr), str, INET_ADDRSTRLEN) != NULL) {
        printf("  Address: %s\n", str);
    } else {
        perror("inet_ntop");
    }
}
int main() {
    struct ifaddrs *ifaddrs_ptr = NULL;
    struct ifaddrs *ifa = NULL;
    // 1. 获取接口地址信息链表
    if (getifaddrs(&ifaddrs_ptr) == -1) {
        perror("getifaddrs");
        return -1;
    }
    // 2. 遍历链表
    for (ifa = ifaddrs_ptr; ifa != NULL; ifa = ifa->ifa_next) {
        // 我们只关心已开启的接口
        if (!(ifa->ifa_flags & IFF_UP)) {
            continue;
        }
        printf("Interface: %s\n", ifa->ifa_name);
        printf("  Flags: %u\n", ifa->ifa_flags);
        // 处理 IPv4 地址
        if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
            struct sockaddr_in *addr_in = (struct sockaddr_in *)ifa->ifa_addr;
            printf("  Family: AF_INET (IPv4)\n");
            print_ipv4_info(addr_in);
            // 处理子网掩码
            if (ifa->ifa_netmask) {
                struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask;
                printf("  Netmask: %s\n", inet_ntoa(netmask_in->sin_addr));
            }
        }
        // 处理 IPv6 地址 (示例中省略,但原理相同)
        else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
            printf("  Family: AF_INET6 (IPv6)\n");
            // 可以添加打印 IPv6 地址的代码
        }
        printf("-------------------------------------\n");
    }
    // 3. 释放内存
    freeifaddrs(ifaddrs_ptr);
    return 0;
}

编译和运行

将上述代码保存为 list_interfaces.c,然后使用 GCC 编译:

gcc list_interfaces.c -o list_interfaces

运行程序:

c语言ifaddrstruct
(图片来源网络,侵删)
./list_interfaces

可能的输出示例:

Interface: lo
  Flags: 8195
  Family: AF_INET (IPv4)
  Address: 127.0.0.1
  Netmask: 255.0.0.0
-------------------------------------
Interface: eth0
  Flags: 65535
  Family: AF_INET (IPv4)
  Address: 192.168.1.101
  Netmask: 255.255.255.0
-------------------------------------
Interface: docker0
  Flags: 4099
  Family: AF_INET (IPv4)
  Address: 172.17.0.1
  Netmask: 255.255.0.0
-------------------------------------
特性 描述
用途 获取本地系统中所有网络接口的详细信息。
头文件 <ifaddrs.h>
核心结构 struct ifaddrs,通过 ifa_next 组成链表。
关键函数 getifaddrs() (获取链表), freeifaddrs() (释放内存)。
地址处理 ifa_addr 是通用指针,需根据 sa_family (如 AF_INET, AF_INET6) 转换为具体类型 (sockaddr_in, sockaddr_in6) 后才能使用。
优点 标准、强大,能同时获取 IPv4 和 IPv6 信息,是网络编程的基础工具。
注意事项 必须调用 freeifaddrs() 释放内存,否则会导致内存泄漏。

ifaddrs 是 C 语言进行底层网络编程时不可或缺的工具,理解它的使用方法对于编写网络诊断工具、服务器程序等非常有帮助。

-- 展开阅读全文 --
头像
织梦 robots.txt
« 上一篇 01-03
织梦dedecms商城模板
下一篇 » 01-03

相关文章

取消
微信二维码
支付宝二维码

目录[+]