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));