最近與IP地址以及socket 地址打交道比較多, 碰到幾個常見的套接字:
-
sockaddr_in
- ipv4 地址, number表示法 4個字節(jié)即可, 比如字面量表示法是"255.255.255.255", 轉(zhuǎn)成數(shù)字表示法,ff.ff.ff.ff
=>ffffffff
-
sock_addr_in6
-- ipv6地址, number表示法 16個字節(jié)即可 -
sockaddr
-- 常見socket API的 socket 地址通用表示法, 16個字節(jié) -
sockaddr_storage
-- 為兼容ipv6的socket地址通用表示法, 28 個字節(jié)
IPv4 address
以及 Socket address for IPv4
, in_addr
是4個字節(jié), IPv4 的Socket address
是16個字節(jié)
typedef __uint8_t sa_family_t;
typedef __uint16_t in_port_t;
typedef __uint32_t in_addr_t; /* base type for internet address */
/*
* Internet address (a structure for historical reasons)
* Ipv4 的地址, 歷史原因
*/
struct in_addr {
in_addr_t s_addr;
};
/*
* Socket address, internet style.
*/
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
IPv6 address
以及 Socket address for IPv6
, IPv6 的Socket address
是28個字節(jié)
typedef __uint8_t sa_family_t;
typedef __uint16_t in_port_t;
/*
* IPv6 address 16個字節(jié) 128bit
*/
typedef struct in6_addr {
union {
__uint8_t __u6_addr8[16];
__uint16_t __u6_addr16[8];
__uint32_t __u6_addr32[4];
} __u6_addr; /* 128-bit IP6 address */
} in6_addr_t;
#define s6_addr __u6_addr.__u6_addr8
#define INET6_ADDRSTRLEN 46
/*
* Socket address for IPv6
*/
struct sockaddr_in6 {
__uint8_t sin6_len; /* length of this struct(sa_family_t) */
sa_family_t sin6_family; /* AF_INET6 (sa_family_t) */
in_port_t sin6_port; /* Transport layer port # (in_port_t) */
__uint32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
__uint32_t sin6_scope_id; /* scope zone index */
};
為了統(tǒng)一各種協(xié)議, socket對應(yīng)的接口就定義了兩個通用結(jié)構(gòu), 分別是sockaddr(16字節(jié))
和sockaddr_storage(128字節(jié))
,兩個通用套接字的結(jié)構(gòu)體, 其中sockaddr_storage
是為了適配sockaddr_in6(128字節(jié))
這樣長度比較大的協(xié)議而后來定義的闽颇,如果需要用到sockaddr_storage
這樣的通用套接字我擂,則強轉(zhuǎn)為sockaddr
,并且長度用sizeof(struct sockaddr_storage)
下圖中, struct sockaddr
是16個字節(jié), struct sockaddr_storage
是28個字節(jié)
/*
* [XSI] Structure used by kernel to store most addresses.
*/
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
/*
* RFC 2553: protocol-independent placeholder for socket addresses
*/
#define _SS_MAXSIZE 128
#define _SS_ALIGNSIZE (sizeof(__int64_t))
#define _SS_PAD1SIZE \
(_SS_ALIGNSIZE - sizeof(__uint8_t) - sizeof(sa_family_t))
#define _SS_PAD2SIZE \
(_SS_MAXSIZE - sizeof(__uint8_t) - sizeof(sa_family_t) - \
_SS_PAD1SIZE - _SS_ALIGNSIZE)
/*
* [XSI] sockaddr_storage
*/
struct sockaddr_storage {
__uint8_t ss_len; /* address length */
sa_family_t ss_family; /* [XSI] address family */
char __ss_pad1[_SS_PAD1SIZE];
__int64_t __ss_align; /* force structure storage alignment */
char __ss_pad2[_SS_PAD2SIZE];
};
在Linux 網(wǎng)絡(luò)編程中, 不管用的是什么協(xié)議, 使用的都是同一套socket API, 而每個協(xié)議表示網(wǎng)絡(luò)地址的結(jié)構(gòu)體是不一樣的, 如IPv4是struct sockaddr_in
, IPv6是struct sockaddr_in6
, 所以需要將所有這些表示網(wǎng)絡(luò)地址的結(jié)構(gòu)體抽象成一個通用的結(jié)構(gòu)體, 抽象之后的結(jié)構(gòu)體便是struct sockaddr
.
常見的涉及通用socket結(jié)構(gòu)體的API為:
/*application->kernel*/
int bind (int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
int connect(int sockfd, const struct sockaddr *serv_addr,
socklen_t addrlen);
int sendto (int s, const void *msg, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen);
/*kernel->application*/
int accept (int s, struct sockaddr *addr, socklen_t *addrlen);
int recvfrom (int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
那為什么會出現(xiàn)struct sockaddr_storage
這個通用結(jié)構(gòu)體呢齐蔽?
身為一個通用的地址數(shù)據(jù)結(jié)構(gòu), 它的大小得要是所有具體協(xié)議地址結(jié)構(gòu)大小的最大值, 可是sizeof(struct sockaddr) = 16
, 而sizeof(struct sockaddr_in6) = 28
, 顯然struct sockaddr
這個通用的數(shù)據(jù)結(jié)構(gòu)hold不住IPv6啊,所以struct sockaddr_storage
這個新結(jié)構(gòu)橫空出世了舆驶,它的大小為128字節(jié)拓轻,應(yīng)該能裝得下目前所以協(xié)議的地址結(jié)構(gòu)了。
在涉及多種協(xié)議的網(wǎng)絡(luò)編程中批幌,用struct sockaddr_storage
這個結(jié)構(gòu)體來表示通信地址础锐,調(diào)用socket API時,需要將struct sockaddr_storage
強制類型轉(zhuǎn)換為struct sockaddr
荧缘,地址長度為sizeof(struct sockaddr_storage)
皆警。如:
struct sockaddr_storage addr;
/*
地址賦值
*/
sendto (s, *msg, len, flags, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
一個IPv4/IPv6混合編程的Demo:
struct sockaddr_storage addr;
memset(&addr, 0, sizeof(struct sockaddr_storage));
if (isIPv6 == TRUE)
{
struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)&addr;
addr_v6->sin6_family = AF_INET6;
addr_v6->sin6_port = 1234;
inet_pton(AF_INET6, “2001:3211::1”, &(addr_v6->sin6_addr));
}
else
{
struct sockaddr_in *addr_v4 = (struct sockaddr_in *)&addr;
addr_v4->sin_family = AF_INET;
addr_v4->sin_port = 1234;
inet_aton(“192.168.1.228”, &(addr_v4->sin_addr));
}
sendto(sock, buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
參考文獻
https://www.cnblogs.com/tanghuimin0713/p/3425936.html
https://blog.csdn.net/weixin_44874963/article/details/89395292