1. IPv4: struct sockaddr_in, 16个字节
1 struct sockaddr_in { 2 sa_family_t sin_family; /* AF_INET */
3 in_port_t sin_port; /* Port number. */
4 struct in_addr sin_addr; /* Internet address. */
5
6 /* Pad to size of `struct sockaddr'. */
7 unsigned char sin_zero[sizeof (struct sockaddr) -
8 sizeof (sa_family_t) -
9 sizeof (in_port_t) -
10 sizeof (struct in_addr)]; 11 }; 12 typedef uint32_t in_addr_t; 13 struct in_addr { 14 in_addr_t s_addr; /* IPv4 address */
15 };
2. IPv6: struct sockaddr_in6, 28个字节
1 struct sockaddr_in6 { 2 sa_family_t sin6_family; /* AF_INET6 */
3 in_port_t sin6_port; /* Transport layer port # */
4 uint32_t sin6_flowinfo; /* IPv6 flow information */
5 struct in6_addr sin6_addr; /* IPv6 address */
6 uint32_t sin6_scope_id; /* IPv6 scope-id */
7 }; 8 struct in6_addr { 9 union { 10 uint8_t u6_addr8[16]; 11 uint16_t u6_addr16[8]; 12 uint32_t u6_addr32[4]; 13 } in6_u; 14
15 #define s6_addr in6_u.u6_addr8
16 #define s6_addr16 in6_u.u6_addr16
17 #define s6_addr32 in6_u.u6_addr32
18 };
3. 通用结构体1: struct sockaddr, 16个字节
1 struct sockaddr { 2 sa_family_t sa_family; /* Address family */
3 char sa_data[14]; /* protocol-specific address */
4 };
4. 通用结构体2: struct sockaddr_storage,128个字节
1 /* Structure large enough to hold any socket address 2 (with the historical exception of AF_UNIX). 128 bytes reserved. */ 3 4 #if ULONG_MAX > 0xffffffff 5 # define __ss_aligntype __uint64_t 6 #else 7 # define __ss_aligntype __uint32_t 8 #endif 9 #define _SS_SIZE 128 10 #define _SS_PADSIZE (_SS_SIZE - (2 * sizeof (__ss_aligntype))) 11 12 struct sockaddr_storage 13 { 14 sa_family_t ss_family; /* Address family */ 15 __ss_aligntype __ss_align; /* Force desired alignment. */ 16 char __ss_padding[_SS_PADSIZE]; 17 };
为什么会有通用结构体且为什么会有两个通用结构体呢?以下纯属个人见解。
网络通信中涉及到很多协议,有IPv4, IPv6, IPX, X.25, AX.25......(socket函数的domain参数表示协议族)。在网络编程中,不管用的是什么协议,使用的都是同一套socket API,而每个协议表示网络地址的结构体是不一样的,如IPv4是struct sockaddr_in,IPv6是struct sockaddr_in6,所以需要将所有这些表示网络地址的结构体抽象成一个通用的结构体,抽象之后的结构体便是struct sockaddr。(具体协议的地址结构与通用的地址结构的关系有点类似于面向对象编程中对象与类的关系,我没学过面向对象编程,只是隐约感觉这两者思想有点类似)
涉及到struct sockaddr的sock API有:
1 /*application->kernel*/
2 int bind (int sockfd, struct sockaddr *my_addr, socklen_t addrlen); 3
4 int connect(int sockfd, const struct sockaddr *serv_addr, 5 socklen_t addrlen); 6
7 int sendto (int s, const void *msg, size_t len, int flags, 8 const struct sockaddr *to, socklen_t tolen); 9
10 /*kernel->application*/
11 int accept (int s, struct sockaddr *addr, socklen_t *addrlen); 12
13 int recvfrom (int s, void *buf, size_t len, int flags, 14 struct sockaddr *from, socklen_t *fromlen); 15
16 int getpeername(int s, struct sockaddr *name, socklen_t *namelen); 17 int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
那为什么会出现struct sockaddr_storage这个通用结构体呢?
身为一个通用的地址数据结构,它的大小得要是所有具体协议地址结构大小的最大值,可是sizeof(struct sockaddr) = 16, 而sizeof(struct sockaddr_in6) = 28, 显然struct sockaddr这个通用的数据结构hold不住IPv6啊,所以struct sockaddr_storage这个新结构横空出世了,它的大小为128字节,应该能装得下目前所以协议的地址结构了。
在涉及多种协议的网络编程中,用struct sockaddr_storage这个结构体来表示通信地址,调用socket API时,需要将struct sockaddr_storage强制类型转换为struct sockaddr,地址长度为sizeof(struct sockaddr_storage)。如:
1 struct sockaddr_storage addr; 2 /*
3 地址赋值 4 */
5 sendto (s, *msg, len, flags, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
也许有人会问,为什么不直接把API中的struct sockaddr改成struct sockaddr_storage?每次用的时候还要强制类型转换多麻烦。
一个API定义后,参数是不能改变的,不然老代码就无法运行了。
也许还有人会问,为什么一开始不把struct sockaddr的大小定义得大一点?
定义struct sockaddr的时候,还在IPv4时代,定义struct sockaddr的人没有预料到未来会有IPv6的诞生,所以将struct sockaddr定义成了跟struct sockaddr_in一样的大小。
IPv4/IPv6混合编程示例:
1 struct sockaddr_storage addr; 2 memset(&addr, 0, sizeof(struct sockaddr_storage)); 3 if (isIPv6 == TRUE) 4 { 5 struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)&addr; 6 addr_v6->sin6_family = AF_INET6; 7 addr_v6->sin6_port = 1234; 8 inet_pton(AF_INET6, “2001:3211::1”, &(addr_v6->sin6_addr)); 9 } 10 else
11 { 12 struct sockaddr_in *addr_v4 = (struct sockaddr_in *)&addr; 13 addr_v4->sin_family = AF_INET; 14 addr_v4->sin_port = 1234; 15 inet_aton(“192.168.1.228”, &(addr_v4->sin_addr)); 16 } 17
18 sendto(sock, buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));