在套接字编程中, 一个网络连接的两端是通过套接字标识的,
而套接字的内容包括通信两端主机的 IP 地址和端口号、协议族等填充。
这些内容需要保存在对应协议的套接字地址结构中,比如
IPv4 的套接字地址结构,定义在<\netinet/in.h>:
struct sockaddr_in{
uint8_t sin_len; // length of structure: 16
sa_family_t sin_family; // AF_INET
in_port_t sin_port; // 16-bit TCP or UDP port number, network ordered
struct in_addr sin_addr; // 32-bit IPv4 address, network ordered
char sin_zero[8]; // unused
}
struct in_addr{
in_addr_t s_addr; // 32-bit IPv4 address, network byte ordered
}
IPv6 的套接字地址结构,定义在<\netinet/in.h>:
#define SIN6_LEN
struct sockaddr_in6{
uint8_t sin6_len; // length of structure: 28
sa_family_t sin6_family; // AF_INET6
in_port_t sin6_port; // transport layer port, network ordered
uint32_t sin6_flowinfo // flow information, undefined
struct in6_addr sin6_addr; // IPv6 address, network ordered
uint32_t sin6_scope_id; // set of interfaces for a scope
}
struct in6_addr{
uint8_t s6_addr[16]; // 128-bit IPv6 address, network byte ordered
}
那为什么会需要一个通用套接字地址结构?分析如下:
套接字函数的套接字地址结构参数是以指针形式传入,比如
int bind(int, struct sockaddr *, socklen_t);
而不同协议(IPv4,IPv6)的地址结构类型不同,因此为了兼容两者必须使用一个统一的类型,一般情况下我们可以使用 void * 类型来作为这些套接字函数的入参(在网络编程的时候直接将特定协议的套接字地址结构强制转换为 void ),但是 void 类型是在 ANSI C 标准中定义的,而套接字函数的定义早于 ANSI C 标准,因此只能单独定义一个通用套接字类型来兼容多个协议类型。
通用套接字地址结构,定义在<\sys/socket.h>:
struct sockaddr{
uint8_t sa_len;
sa_family_t sa_family; // address family: AF_xxx value
char sa_data[14]; // protocol-specific address
}
最新通用套接字地址结构,定义在<\netinet/in.h>:
struct sockaddr_storage{
uint8_t ss_len; // length of this struct: implementation dependent
sa_family_t ss_family; // address family: AF_xxx value
}
很明显,在使用这些套接字函数是我们需要强制转换特定协议的套接字地址结构为通用套接字地址结构。