ifreq socketaddr sockaddr_in实例详解

前言

  网络编程是程序连接网络拓展的基础,尤其是在物联网、互联网加等概念火热的当下,网络编程能力体现了一个程序员能否具有大型程序的开发能力。这里通过详细例子解析structifreqstructsockaddr_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、广播地址、子网掩码、网卡硬件地址的,这几项在网络编程中使用频率很高,最好熟练掌握。

Socketaddrsocketaddr_in

  Socketaddrsocketaddr_in#include <sys/socket.h>#include<netinet/in.h>头文件定义。ifreq结构体包含了Socketaddr,为了搞清楚他们之间的联系,有必要掌握Socketaddr结构体的内容定义。Socketaddr结构体有16个字符的长度,包含sa_familysa_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指定了32IP地址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同样大小 */ 
};

   细心的读者可能发现了,Socketaddrsocketaddr_in具有相同的长度,相同定义了地址族等参数,其实它们在一定情况下是通用的(下面会举例说明),可以对它们进行memcpymemcmp等操作,不过在实际应用中,最好对它们进行类型转换,以免编译器报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;
}

总结

  物联网、互联网加的蓬勃发展,产品的功能实现越来越依赖网络,掌握一些网络编程的小技巧,可以使得我们在产品开发中能够事半功倍,提升软实力。今天是520日,在特殊的日子里整理一下知识,以做备忘,原创不易,转载说明出处。番外篇:以上例子我整合成了一个网络小工具,在Linux主流平台编译运行没有任何的warning,挺实用的,需要的朋友点此下载







猜你喜欢

转载自blog.csdn.net/dosthing/article/details/80378947
今日推荐