参考文章:http://blog.csdn.net/baobaoyeye/article/details/6175456
看APUE中的网络IPC这章,其中讲到地址格式
通用的地址结构sockaddr
struct sockaddr{
sa_family_t sa_familiy; /* address family */
char sa_data[]; /* variable-length address */
...
...
};
实现可以自由地添加额外的成员并且定义sa_data成员的大小。
在IPV4因特网域(AF_INET)中,套接字地址用结构sockaddr_in表示:
struct in_addr {
in_addr_t s_addr; /* IPv4 address */
};
struct sockaddr_in {
sa_family_t sin_family; /* address family */
in_port_t sin_port; /* port number */
struct in_addr sin_addr; /* IPv4 address */
};
IPv6因特网域(AF_INET6)套接字地址用如下结构sockaddr_in6表示:
struct in6_addr {
uint8_t s6_addr[16]; /* IPv6 address */
};
struct sockaddr_in6 {
sa_family_t sin6_addr[16]; /* address family */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* traffic class and flow info */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for scope */
};
以上是Single UNIX Specification必须的定义,每个实现可以自由的添加额外的字段。
下面考察Linux中的实现,在Linux编程时,使用sockaddr结构时,要#include <sys/socket.h>,
因此查看/urs/include/sys/socket.h内容,看到
/* This operating system-specific header file defines the SOCK_*, PF_*,
AF_*, MSG_*, SOL_*, and SO_* constants, and the `struct sockaddr',
`struct msghdr', and `struct linger' types. */
#include <bits/socket.h>
#ifdef __USE_BSD
/* This is the 4.3 BSD `struct sockaddr' format, which is used as wire
format in the grotty old 4.3 `talk' protocol. */
struct osockaddr
{
unsigned short int sa_family;
unsigned char sa_data[14];
};
#endif
对 #include <bits/socket.h>的注释中说了,sturct sockaddr实际上是定义在bits/socket.h中的
(后面的osockaddr结构是4.3BSD中的sockaddr结构,可以看出SUS规定的和该结构完全一致,
实际上,UNIX和Linux上的socket实现都是从BSD的socket实现演变过来的。)
我们再查看bit/socket.h的内容,找到
/* Get the definition of the macro to define the common sockaddr members. */
#include <bits/sockaddr.h>
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
其中出现了一个宏__SOCKADDR_COMMON(),根据第1、2行到文件/usr/include/bits/sockaddr.h
中去查找该宏,得到
/* POSIX.1g specifies this type name for the `sa_family' member. */
typedef unsigned short int sa_family_t;
/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc. */
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
第二行定义了sa_family_t类型,该类型在linux中就是无符号短整型,用来表示地址类型;
第八行定义了__SOCKADDR_COMMON(sa_prefix)宏,其中出现了预定义宏##,该预定义宏的
作用见另一篇博文();
宏__SOCKADDR_COMMON(sa_prefix)定义了一个以sa_prefix和family合并起来为变量名的
变量,该变量类型为sa_family_t。
宏__SOCKADDR_COMMON(sa_prefix)展开得到
sa_family_t sa_familiy;
和SUS要求的一致。
在考察linux中的sockaddr_in和sockadd_in6,使用这两个结构,应该#include <netinet/in.h>
(apra/inet.h包含了netinet/in.h,使用#include <apra/inet.h>的话就不用包含后者了。)
于是在/usr/include/netinet/in.h中查到到:
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
/* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};
其中sockaddr_in6和SUS要求的完全一样,sockaddr_in多了一个成员
该成员中有一个宏定义__SOCKADDR_COMMON_SIZE,从字面意思看应该时__SOCKADDR_COMMON
的大小,而前面看到宏__SOCKADDR_COMMON()实际上定义了一个sa_family_t类型(在linux中实现
为无符号短整形)的变量
前面在/usr/include/bits/sockaddr.h中看到该宏定义如下:
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
验证了上述猜测。
多出的一个成员起填充字段的作用,为了将sockaddr_in结构补全至和sockaddr结构的大小一致。
这样,sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向
sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,
然后用进行类型转换就可以了。
struct sockaddr_in *sinp;
/* ai_addr为从某个函数返回sockaddr结构的指针 */
sinp = (struct sockaddr_in *)ai_addr;
在看看linux中的in_addr和in6_addr结构,在/usr/include/netinet/in.h中:
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
...
...
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
ipv6地址结构中用到了共用体,其中三个变量都占128位(都是同一个IPv6地址,只是访问字节数不一样)。