如果我使用 struct sockaddr_in,则地址族已在我创建的任何套接字中指定。
此外,如果 sin_family 成员必须始终是 AF_INET,那么使用它似乎是多余的。
那么为什么sin_family成员存在呢?
确实,sin_family的成员struct sockaddr_in必须始终是AF_INET。这样做的原因是为了可以知道通用套接字描述符的实际类型。
struct sockaddr_in是几种类型的套接字地址描述符之一,根据网络协议具有不同的成员:
struct sockaddr_in {
sa_family_t sin_family; /* always AF_INET */
in_port_t sin_port;
struct in_addr sin_addr;
};
struct sockaddr_in6 {
sa_family_t sin6_family; /* always AF_INET6 */
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct sockaddr_un {
sa_family_t sun_family; /* always AF_UNIX */
char sun_path[108];
};
/* and more ... */
Run Code Online (Sandbox Code Playgroud)
一些 POSIX 函数如bind和accept接受一个指向 的指针struct sockaddr,这是一个只有初始sa_family_t sa_family;成员的虚拟类型。const因此,它们或其他代码可以通过检查协议族来处理转换为 [ ]指针的任何一个结构struct sockaddr*,然后根据该值转换回实际类型。
例如,日志记录包装器bind可能会执行以下操作:
int loggable_bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen)
{
bool do_log = logging_enabled();
if (do_log) {
switch (addr->sa_family) {
case AF_INET: {
const struct sockaddr_in* addr_in = (const struct sockaddr_in*) addr;
printf("bind: fd=%d family=AF_INET addr=%s port=%u",
sockfd,
inet_ntoa(addr_in->sin_addr),
(unsigned) addr_in->sin_port);
break;
}
case AF_INET6: {
const struct sockaddr_in6* addr_in6 = (const struct sockaddr_in6*) addr;
printf("bind: fd=%d family=AF_INET6 addr=%.*s port=%u",
sockfd,
sizeof(addr_in6->sin6_addr.s6_addr),
(const char*) addr_in6->sin6_addr.s6_addr,
(unsigned) addr6_in->sin6_port);
break;
}
case AF_UNIX: {
const struct sockaddr_un* addr_un = (const struct sockaddr_un*) addr;
if (addrlen == sizeof(sa_family_t)) {
printf("bind: fd=%d family=AF_UNIX Unnamed");
} else if (addr_un->sun_path[0] == '\0') {
printf("bind: fd=%d family=AF_UNIX Abstract, path='");
for (size_t i=1; i<(addrlen-offsetof(struct sockaddr_un, sun_path)); ++i) {
int c = (unsigned char) addr_un->sun_path[i];
if (isprint(c))
putchar(c);
else
printf("\\x%02x", (unsigned) c);
}
putchar('\'');
} else {
printf("bind: fd=%d family=AF_UNIX Named, path=%s",
fd, addr_un->sun_path);
}
break;
}
default:
printf("bind: fd=%d family=%u", fd, (unsigned) addr->sa_family);
break;
}
}
int result = bind(fd, addr, addrlen);
if (do_log) {
int local_err = errno;
printf(" -> %d\n", result);
if (result < 0)
printf(" errno=%d: %s\n", local_err, strerror(local_err));
}
return result;
}
Run Code Online (Sandbox Code Playgroud)