前言
网络编程是程序连接网络拓展的基础,尤其是在物联网、互联网加等概念火热的当下,网络编程能力体现了一个程序员能否具有大型程序的开发能力。这里通过详细例子解析structifreq和structsockaddr_in结构体,这两个结构体通常是配合使用,建立socket连接然后把ifeq作为数据源调用ioctl函数与内核交互,通过数据类型转换,将ifeq数据内容赋值给sockaddr_in,进而能实现获取或设置IP地址、MAC地址、子网地址、广播地址等网络参数。
struct ifreq
struct ifreq结构体使用时需要加入头文件#include<net/if.h>,跟头文件查询其定义,它由一个字符数组和共用体构成,以下是它的声明:
struct ifreq {
char ifr_name[IFNAMSIZ]; //interface name
union
{
struct sockaddr ifru_addr; //address
struct sockaddr ifru_dstaddr; //other wnd of p-p Ink
struct sockaddr ifru_broadaddr; //broadcast address
struct sockaddr ifru_netmask; //interface net mask
struct sockaddr ifru_hwaddr; //MAC address
short int ifru_flags; //flags
int ifru_ivalue; //link bandwith
int ifru_mtu; //mtu
struct ifmap ifru_map; //device man
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ]; //new name
__caddr_t ifru_data; //for use by interface
} ifr_ifru;
};
其中ifr_name是用来配置网卡的,ifru_addr、ifru_broadaddr、ifru_netmask、ifru_hwaddr则分别是配置IP、广播地址、子网掩码、网卡硬件地址的,这几项在网络编程中使用频率很高,最好熟练掌握。
Socketaddr与socketaddr_in
Socketaddr与socketaddr_in在#include <sys/socket.h>、#include<netinet/in.h>头文件定义。ifreq结构体包含了Socketaddr,为了搞清楚他们之间的联系,有必要掌握Socketaddr结构体的内容定义。Socketaddr结构体有16个字符的长度,包含sa_family和sa_data[14] 两个元素,分别定义了地址族和协议地址内容。
struct sockaddr
{
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字节的协议地址 */
};
再来看看socketaddr_in结构体,它也具有16个字符的长度,内容相比Socketaddr结构体要具体些,由sin_family指定地址族,sin_port指定端口号,sin_addr指定了32位IP地址,sin_zero[8]用来填充结构体。
struct sockaddr_in
{
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* 32位IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
};
细心的读者可能发现了,Socketaddr与socketaddr_in具有相同的长度,相同定义了地址族等参数,其实它们在一定情况下是通用的(下面会举例说明),可以对它们进行memcpy、memcmp等操作,不过在实际应用中,最好对它们进行类型转换,以免编译器报warning。不过问题也来了,它们既然是通用的,为什么还要分别定义呢,这就涉及协议的兼容性问题,我们知道,数据在计算机都是01表示的,其实这两个结构体存储的内容是一样的,也就是说具有相同的01数据结构。使用Socketaddr兼容性好,使用socketaddr_in方则便数据引用。
简单例子
/*
函数说明:GetNetInfo是用来获取我们网卡信息,如IP、MAC、MASK等
函数参数:device指明网卡名称,如eth0
*/
int GetNetInfo(char *device)
{
struct sockaddr_in sin;
struct sockaddr_in netmask;
struct sockaddr_in broad;
struct ifreq ifr;
char ipaddr[20]={0};
unsigned char macaddr[6]={0};/*macaddr must be unsigned*/
char maskaddr[20]={0};
char broadaddr[20]={0};
int sock = 0;
memset(&ifr, 0, sizeof(ifr));
if((device == NULL) || (*device == '\0'))
{
printf("net device == NULL\r\n");
return -1;
}
strncpy(ifr.ifr_name, device, IFNAMSIZ);
sock = socket(AF_INET, SOCK_DGRAM, 0); //Create Socket
if(sock <= 0)
{
debugpri("Get ip: sock error, %s\r\n", strerror(errno));
return -1;
}
if(ioctl(sock,SIOCGIFADDR,&ifr))/*get ip*/
{
debugpri("ioctl ip: sock error, %s\r\n", strerror(errno));
return -1;
}
memcpy(&sin,&ifr.ifr_addr,sizeof(sin));
sprintf(ipaddr,"%s",inet_ntoa(sin.sin_addr));
if(ioctl(sock,SIOCGIFHWADDR,&ifr))/*get mac*/
{
debugpri("ioctl mac: sock error, %s\r\n", strerror(errno));
return -1;
}
memcpy(macaddr,ifr.ifr_hwaddr.sa_data,6);
if(ioctl(sock,SIOCGIFNETMASK,&ifr))/*get netmask*/
{
debugpri("ioctl netmask: sock error, %s\r\n", strerror(errno));
return -1;
}
memcpy(&netmask,&ifr.ifr_netmask,sizeof(netmask));
sprintf(maskaddr,"%s",inet_ntoa(netmask.sin_addr));
if(ioctl(sock,SIOCGIFBRDADDR,&ifr))/*get broadcast*/
{
debugpri("ioctl broadcast: sock error, %s\r\n", strerror(errno));
return -1;
}
memcpy(&broad,&ifr.ifr_broadaddr,sizeof(broad));
sprintf(broadaddr,"%s",inet_ntoa(broad.sin_addr));
/*output the net device info*/
printf("IP: %s\n",ipaddr);
printf("HWaddr:%02X:%02X:%02X:%02X:%02X:%02X\n",macaddr[0],macaddr[1],macaddr[2],macaddr[3],macaddr[4],macaddr[5]);
printf("Mask: %s\n",maskaddr);
printf("Bcast: %s\n",broadaddr);
close(sock);
return 0;
}
进阶例子
//函数说明:设置网卡ip地址
int SetNetIP(char *device,char *ipaddr)
{
int sock = 0;
struct ifreq ifr;
struct sockaddr_in si;
if((device == NULL) || (*device == '\0') || (ipaddr == NULL))
{
printf("set ip: netdevice or ip is NULL\r\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock <= 0)
{
debugpri("set ip: sock error, %s\r\n", strerror(errno));
return -1;
}
memset(&si, 0, sizeof(struct sockaddr_in));
si.sin_family = PF_INET;
si.sin_addr.s_addr = inet_addr((char *)ipaddr);
memcpy(&ifr.ifr_addr, &si, sizeof(struct sockaddr_in));
if(ioctl(sock, SIOCSIFADDR, &ifr) < 0)
{
debugpri("set ip(%d:%d:%d:%d): %s\r\n",(si.sin_addr.s_addr)&0xff,(si.sin_addr.s_addr>>8)&0xff,(si.sin_addr.s_addr>>16)&0xff,(si.sin_addr.s_addr>>24)&0xff,strerror(errno));
close(sock);
return -1;
}
else
{
printf("set ip(%d:%d:%d:%d) success!\r\n",(si.sin_addr.s_addr)&0xff,(si.sin_addr.s_addr>>8)&0xff,(si.sin_addr.s_addr>>16)&0xff,(si.sin_addr.s_addr>>24)&0xff);
}
close(sock);
return 0;
}
//函数说明:设置网卡子网掩码地址
int SetNetMask(char *device,char *maskaddr)
{
int sock = 0;
struct ifreq ifr;
struct sockaddr_in si;
if((device == NULL) || (*device == '\0') || (maskaddr == NULL))
{
printf("set ip: netdevice or ip is NULL\r\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock <= 0)
{
printf("set mask: sock error, %s\r\n", strerror(errno));
return -1;
}
memset(&si, 0, sizeof(struct sockaddr_in));
si.sin_family = PF_INET;
si.sin_addr.s_addr = inet_addr((char *)maskaddr);
memcpy(&ifr.ifr_netmask, &si, sizeof(struct sockaddr_in));
if(ioctl(sock, SIOCSIFNETMASK, &ifr) < 0)
{
printf("set mask(%d:%d:%d:%d):%s\r\n",(si.sin_addr.s_addr)&0xff,(si.sin_addr.s_addr>>8)&0xff,(si.sin_addr.s_addr>>16)&0xff,(si.sin_addr.s_addr>>24)&0xff,strerror(errno));
close(sock);
return -1;
}
else
{
printf("set mask(%d:%d:%d:%d): success\r\n",(si.sin_addr.s_addr)&0xff,(si.sin_addr.s_addr>>8)&0xff,(si.sin_addr.s_addr>>16)&0xff,(si.sin_addr.s_addr>>24)&0xff);
}
close(sock);
return 0;
}
//函数说明:设置网卡开启和关闭
int netSetStatus(char *device,char *cmd)
{
int sock = 0;
struct ifreq ifr;
if((device == NULL) || (*device == '\0'))
{
printf("set ip: device == NULL\r\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, device, IFNAMSIZ);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock <= 0)
{
debugpri("set ip: sock error, %s\r\n", strerror(errno));
return -1;
}
if(ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
{
debugpri("unknown interface: %s\n", device);
close(sock);
return -1;
}
if(strcmp(cmd,"down") == 0)
{
ifr.ifr_flags &= ~IFF_UP;
}
else if(strcmp(cmd,"up") == 0)
{
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
}
else
{
printf("unknown command: %s\n",cmd);
close(sock);
return -1;
}
if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
{
printf("set netcard status %s error\n",cmd);
close(sock);
return -1;
}
close(sock);
return 0;
}
总结
物联网、互联网加的蓬勃发展,产品的功能实现越来越依赖网络,掌握一些网络编程的小技巧,可以使得我们在产品开发中能够事半功倍,提升软实力。今天是5月20日,在特殊的日子里整理一下知识,以做备忘,原创不易,转载说明出处。番外篇:以上例子我整合成了一个网络小工具,在Linux主流平台编译运行没有任何的warning,挺实用的,需要的朋友点此下载。


本文深入解析Linux网络编程中的struct ifreq和struct sockaddr_in结构体,阐述它们在网络参数设置中的作用,如获取或设置IP、MAC、子网和广播地址。通过示例展示了它们在socket连接和ioctl函数中的应用,强调了类型转换和兼容性的重要性,旨在帮助开发者提升网络编程能力。

被折叠的 条评论
为什么被折叠?



