今天阅读一个用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的总占用空间大小,减去第一个变量,第二个变量,第三个变量占用空间的大小就是剩余空间的大小。
分析一句C语言网络编程代码
最新推荐文章于 2021-05-20 14:20:20 发布