分析一句C语言网络编程代码

今天阅读一个用C语言写的读取网卡的IP地址程序,
遇到下面这个把internet地址转换成ASCII类型的函数,我把我分析它的过程写出来:
inet_ntoa(((struct sockaddr_in*)(&buf[inface].ifr_addr))->sin_addr);
首先看inet_ntoa的原型和作用:
在/usr/include/arpa/inet.h中这样定义的
 52 /* Convert Internet number in IN to ASCII representation.  The return value
 53    is a pointer to an internal array containing the string.  */
 54 extern char *inet_ntoa (struct in_addr __in) __THROW;

其中参数是struct in_addr类型;
那我们再来看看下面代码中struct sockaddr_in 的成员sin_addr是不是这个类型
((struct sockaddr_in*)(&buf[inface].ifr_addr))->sin_addr

在/usr/include/netinet/in.h中的定义是这样的:
224 /* Structure describing an Internet socket address.  */
225 struct sockaddr_in 
226   {            
227     __SOCKADDR_COMMON (sin_);
228     in_port_t sin_port;         /* Port number.  */
229     struct in_addr sin_addr;        /* Internet address.  */ 请看这里
230                
231     /* Pad to size of `struct sockaddr'.  */
232     unsigned char sin_zero[sizeof (struct sockaddr) -
233                __SOCKADDR_COMMON_SIZE -
234                sizeof (in_port_t) -
235                sizeof (struct in_addr)];
236   };
看到sin_addr这个成员的确是struct in_addr类型的;
好,现在来分析(struct sockaddr_in*)(&buf[inface].ifr_addr)这部分
其中,buf在程序定义为:struct ifreq buf[MAXINTERFACES];
那我们看一看struct ifreq是怎么定义的:
在/usr/include/net/if.h中是如下定义的:
123 /* Interface request structure used for socket ioctl's.  All interface
124    ioctl's must have parameter definitions which begin with ifr_name.
125    The remainder may be interface specific.  */
126
127 struct ifreq
128   {
129 # define IFHWADDRLEN    6
130 # define IFNAMSIZ   IF_NAMESIZE
131     union
132       {
133     char ifrn_name[IFNAMSIZ];   /* Interface name, e.g. "en0".  */
134       } ifr_ifrn;
135
136     union
137       {
138     struct sockaddr ifru_addr;/* 看到这儿没有*/
139     struct sockaddr ifru_dstaddr;
140     struct sockaddr ifru_broadaddr;
141     struct sockaddr ifru_netmask;
142     struct sockaddr ifru_hwaddr;
143     short int ifru_flags;
144     int ifru_ivalue;
145     int ifru_mtu;
146     struct ifmap ifru_map;
147     char ifru_slave[IFNAMSIZ];  /* Just fits the size */
148     char ifru_newname[IFNAMSIZ];
149     __caddr_t ifru_data;
150       } ifr_ifru;
151   };
152 # define ifr_name   ifr_ifrn.ifrn_name  /* interface name   */
153 # define ifr_hwaddr ifr_ifru.ifru_hwaddr    /* MAC address      */
/* 再看下面这一句 */
154 # define ifr_addr   ifr_ifru.ifru_addr  /* address      */
155 # define ifr_dstaddr    ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */


于是我们可以得知:
&buf[inface].ifr_addr就是&buf[inface].ifr_ifru.ifru_addr的等价形式,取了这个结构体成员的地址,而这个结构体成员的类型就为:struct sockaddr,
然后,再来看看(struct sockaddr_in*)(&buf[inface].ifr_addr)这句话的意思。
在通过上面的等价转换后,实际它是把struct sockaddr类型的指针转换为struct sockaddr_in类型的指针。

接下来我们再看看,它们的转换过程是怎么样的:
在/usr/include/bits/socket.h中,sockaddr的定义是这样的:
173 /* Structure describing a generic socket address.  */
174 struct sockaddr
175   {
176     __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
177     char sa_data[14];       /* Address data.  */
178   };

在/usr/include/netinet/in.h中的sockaddr_in定义是这样的:
224 /* Structure describing an Internet socket address.  */
225 struct sockaddr_in 
226   {            
227     __SOCKADDR_COMMON (sin_);
228     in_port_t sin_port;         /* Port number.  */
229     struct in_addr sin_addr;        /* Internet address.  */
230                
231     /* Pad to size of `struct sockaddr'.  */
232     unsigned char sin_zero[sizeof (struct sockaddr) -
233                __SOCKADDR_COMMON_SIZE -
234                sizeof (in_port_t) -
235                sizeof (struct in_addr)];
236   };
首先,它们的第一个变量都是相同类型的;
接下来看sockaddr_in的第二,三个变量,
在/usr/include/netinet/in.h中类型分别定义如下
96 /* Type to represent a port.  */
97 typedef uint16_t in_port_t;
 
140 /* Internet address.  */
141 typedef uint32_t in_addr_t;
142 struct in_addr
143   {
144     in_addr_t s_addr;
145   };
它们分别是2字节和4字节大小的变量共6字节,
而在sockaddr中char sa_data[14]占14个字节
所以sockaddr_in使用sin_zero函数填充了剩下的8字节,
算法是取sockaddr的总占用空间大小,减去第一个变量,第二个变量,第三个变量占用空间的大小就是剩余空间的大小。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值