Linux网络编程-structifreqioctl()系统调用
- 电脑硬件
- 2025-08-18 09:33:01

struct ifreq
struct ifreq 是一个数据结构,用于各种与接口相关的输入/输出控制 (ioctl) 调用。它的主要用途是在网络编程中获取和设置网络接口的属性。这个结构体在 <net/if.h> 头文件中定义。
以下是 struct ifreq 的一些主要字段和它们的用途:
ifr_name: 一个字符数组,表示接口的名称,如 “eth0”, “wlan0” 等。
ifr_addr: 一个 struct sockaddr 类型的结构,表示接口的地址。
ifr_netmask: 同样是一个 struct sockaddr 类型的结构,表示接口的网络掩码。
ifr_broadaddr: 表示接口的广播地址。
ifr_flags: 表示接口的标志,如 IFF_UP (接口正在运行)、IFF_BROADCAST (接口支持广播)、IFF_PROMISC (接口在混杂模式下)等。
ifr_hwaddr: 表示接口的硬件(通常是 MAC)地址。
ifr_mtu: 表示接口的最大传输单元 (MTU)。
还有其他一些字段,但上面列出的是最常用的。
当我们想查询或设置接口的特定属性时,会填充 struct ifreq 的适当字段,并使用 ioctl() 系统调用。例如,要设置接口为混杂模式,我们会填充 ifr_name 字段并设置 IFF_PROMISC 标志,然后调用 ioctl()。
例如:
struct ifreq ifr; int sockfd; // 创建一个套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); exit(EXIT_FAILURE); } // 设置ifr_name为我们想查询或修改的接口名 strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); // 获取接口标志 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { perror("ioctl"); close(sockfd); exit(EXIT_FAILURE); } // 打印是否处于混杂模式 if (ifr.ifr_flags & IFF_PROMISC) { printf("eth0 is in promiscuous mode\n"); } else { printf("eth0 is not in promiscuous mode\n"); } close(sockfd);这是一个简单的示例,演示了如何检查网络接口(在这种情况下是 “eth0”)是否处于混杂模式。
ioctl() 系统调用
ioctl() 系统调用提供了一种通用的设备驱动程序或接口操作机制,可以用于各种设备特定的操作和控制。它通常用于那些不适合用常规系统调用(如 read(), write(), open() 等)表示的设备特定的操作。
函数原型: int ioctl(int fd, unsigned long request, ...); 参数:fd: 是一个已经打开的文件或套接字的文件描述符。
request: 是设备特定的请求码。这个请求码定义了我们想要执行的特定操作或命令。
…: 这是一个可变参数,取决于特定的 request。它可能是一个指向数据的指针,也可能是一个直接的值。
返回值: ioctl() 的返回值取决于特定的操作。通常,成功返回非负值,失败返回 -1,并设置 errno。 用途:网络编程: ioctl() 经常用于查询和设置网络接口的参数。例如,获取或设置接口的 IP 地址,查询或设置接口的标志等。
设备控制: 对于各种设备(如终端、声音卡、图形卡),ioctl() 可以用于执行特定的操作或查询信息。
文件系统: 在某些情况下,ioctl() 也可用于文件系统操作。
一些例子:获取网络接口的标志:
struct ifreq ifr; int fd = socket(AF_INET, SOCK_DGRAM, 0); strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); ioctl(fd, SIOCGIFFLAGS, &ifr);设置非阻塞套接字:
int flags = fcntl(fd, F_GETFL, 0); ioctl(fd, FIONBIO, &flags);查询音频设备属性:
int volume; ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume); 注意事项:不是所有的设备都支持所有的 ioctl 操作。要知道设备支持哪些操作,需要查阅特定设备的文档或头文件。
错误处理:与其他系统调用一样,使用 ioctl() 时也应进行适当的错误处理。它可能会失败,并设置 errno,所以应该检查它的返回值并相应地处理错误。
可移植性:ioctl() 是一个非常通用且强大的函数,但它经常与特定的设备或平台相关。这意味着在不同的操作系统或硬件平台上,可能需要不同的 ioctl 请求代码或参数。
总之,ioctl() 提供了一种与设备进行交互的方法,允许开发者执行不同的设备特定操作。然而,正确地使用它需要对特定设备的工作方式有深入的了解。
示例
下面让我们用一个简单的例子来展示如何使用 ioctl() 来获取和设置网络接口(例如 eth0)的 IP 地址。
1. 获取 IP 地址 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sockfd; struct ifreq ifr; // 创建一个 socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); exit(1); } // 指定要查询的接口名 strncpy(ifr.ifr_name, "enp5s0", IFNAMSIZ); // 获取接口的 IP 地址 if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) { perror("ioctl - SIOCGIFADDR "); exit(1); } struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr; printf("IP address of enp5s0: %s\n", inet_ntoa(ipaddr->sin_addr)); close(sockfd); return 0; }程序运行结果为:
majn@tiger:~/C_Project/network_project$ ./ioctl_setip IP address of enp5s0: 192.168.31.6 2. 设置 IP 地址(慎用!!!) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sockfd; struct ifreq ifr; struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; // 创建一个 socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); exit(1); } // 指定要设置的接口名 strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); // 设置新的 IP 地址 sin->sin_family = AF_INET; inet_aton("192.168.1.100", &sin->sin_addr); if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { perror("ioctl - SIOCSIFADDR "); exit(1); } printf("IP address of eth0 set to: %s\n", inet_ntoa(sin->sin_addr)); close(sockfd); return 0; }注意: 设置 IP 地址通常需要 root 权限,所以运行上述代码时可能需要使用 sudo。
上述示例中,ioctl() 被用来查询或设置网络接口的 IP 地址。具体的操作由 SIOCGIFADDR(获取地址)和 SIOCSIFADDR(设置地址)指定。
inet_aton()
inet_aton() 函数用于将点分十进制格式的字符串(如 “192.168.1.1”)转换为一个网络字节序的 IPv4 地址。它在 <arpa/inet.h> 头文件中定义。
函数原型: int inet_aton(const char *cp, struct in_addr *inp); 参数: cp: 指向点分十进制格式的 IPv4 地址字符串的指针。inp: 指向一个 struct in_addr 结构体的指针,该结构体用于存储转换后的地址。 返回值: 成功时返回 1,失败时返回 0。与 inet_addr() 相比,inet_aton() 函数提供了更清晰的错误检查机制,因为它明确返回成功或失败的状态,而不是使用特殊的地址值来表示错误。
示例: #include <stdio.h> #include <arpa/inet.h> int main() { const char *ip_string = "192.168.1.1"; struct in_addr ip_addr; if (inet_aton(ip_string, &ip_addr)) { printf("Conversion successful. Network byte order value: 0x%x\n", ip_addr.s_addr); } else { printf("Conversion failed.\n"); } return 0; }上述程序的运行结果为:
majn@tiger:~/C_Project/network_project$ ./inet_aton_demo Conversion successful. Network byte order value: 0x101a8c0在此示例中,我们将点分十进制的 IP 地址字符串 “192.168.1.1” 使用 inet_aton() 转换为 struct in_addr 格式,并输出转换的结果。
注意:inet_aton() 和 inet_ntoa() 是一对,一个用于将字符串转换为二进制格式,另一个用于进行相反的操作。如果您正在寻找一个线程安全的方法或需要处理 IPv6 地址,建议使用 inet_pton() 和 inet_ntop() 函数代替。
inet_ntoa()
inet_ntoa() 是一个网络工具函数,用于将一个网络字节序的 IPv4 地址转换为点分十进制字符串格式。它在 <arpa/inet.h> 头文件中定义。
函数原型: char *inet_ntoa(struct in_addr in); 参数: in: 是一个 struct in_addr 结构体,包含一个 IPv4 地址(在网络字节序中)。 返回值: 返回一个指向一个静态分配的字符串的指针,该字符串表示 IPv4 地址的点分十进制表示形式。因为返回的是一个静态字符串,所以之后的 inet_ntoa() 调用可能会覆盖之前的内容。 注意事项: inet_ntoa() 返回一个指向静态存储区的指针,这意味着每次调用都会覆盖前一次的结果。如果需要保留转换的地址,应该自行复制返回的字符串。考虑到线程安全性,inet_ntoa() 在多线程环境中可能会导致问题。为了避免这种情况,可以考虑使用更现代、线程安全的函数,例如 inet_ntop()。 示例: #include <stdio.h> #include <arpa/inet.h> int main() { struct in_addr ip_addr; ip_addr.s_addr = htonl(0xC0A80101); // 0xC0A80101 = 192.168.1.1 in hexadecimal char *ip_str = inet_ntoa(ip_addr); printf("IP address in dotted-decimal format: %s\n", ip_str); return 0; }在此示例中,我们创建了一个 struct in_addr 结构体,设置了 IP 地址(以网络字节序),然后使用 inet_ntoa() 将它转换为字符串。
上述程序的运行结果为:
majn@tiger:~/C_Project/network_project$ ./inet_ntoa_demo IP address in dotted-decimal format: 192.168.1.1Linux网络编程-structifreqioctl()系统调用由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Linux网络编程-structifreqioctl()系统调用”
上一篇
Qtraise()问题
下一篇
数据响应式原理