前言
网络编程是程序连接网络拓展的基础,尤其是在物联网、互联网加等概念火热的当下,网络编程能力体现了一个程序员能否具有大型程序的开发能力。这里通过详细例子解析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,挺实用的,需要的朋友点此下载。