我看功能,如connect()和bind()用C插座和发现,他们需要一个指向sockaddr结构.我一直在阅读并使你的应用程序独立于AF,使用sockaddr_storage结构指针并将其转换为sockaddr指针是有用的,因为它具有更大的地址所需的额外空间.
我想知道的是,函数connect()和bind()请求sockaddr指针的方法是如何从指针访问数据,指针指向比预期更大的结构.当然,你传递的是你提供它的结构的大小,但是函数用来从指向更大结构的指针获取IP地址的实际语法是struct *sockaddr什么?
这可能是因为我来自OOP语言,但它似乎有点乱,有点乱.
the*_*ole 46
期望指针的函数struct sockaddr可能在向它们sockaddr发送指针时将你发送给它们的指针强制转换为struct sockaddr_storage.通过这种方式,他们就像访问它一样访问它struct sockaddr.
struct sockaddr_storage设计适合a struct sockaddr_in和struct sockaddr_in6
您不创建自己的struct sockaddr,您通常创建一个struct sockaddr_in或一个struct sockaddr_in6取决于您正在使用的IP版本.为了避免试图知道您将使用的IP版本,您可以使用struct sockaddr_storage哪个可以容纳.这将由struct sockaddrconnect(),bind()等函数进行类型转换,并以这种方式访问.
您可以在下面看到所有这些结构(填充是特定于实现的,用于对齐目的):
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
struct sockaddr_in6 {
u_int16_t sin6_family; // address family, AF_INET6
u_int16_t sin6_port; // port number, Network Byte Order
u_int32_t sin6_flowinfo; // IPv6 flow information
struct in6_addr sin6_addr; // IPv6 address
u_int32_t sin6_scope_id; // Scope ID
};
struct sockaddr_storage {
sa_family_t ss_family; // address family
// all this is padding, implementation specific, ignore it:
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,如果函数需要IPv4地址,它将只读取前4个字节(因为它假定结构是类型struct sockaddr.否则它将读取IPv6的完整16个字节).
Ale*_*lke 10
在具有至少一个虚函数的C++类中,给出了TAG.该标签允许您访问dynamic_cast<>()您的类派生的任何类,反之亦然.TAG是允许dynamic_cast<>()工作的东西.或多或少,这可以是数字或字符串......
在C中,我们仅限于结构.但是,也可以为结构分配TAG.事实上,如果你看一下prole在他的答案中发布的所有结构,你会注意到它们都以2个字节(一个无符号的短)开头,它代表了我们称之为地址族的内容.这确切地定义了结构是什么,因此它的大小,字段等.
因此你可以这样做:
int bind(int fd, struct sockaddr *in, socklen_t len)
{
switch(in->sa_family)
{
case AF_INET:
if(len < sizeof(struct sockaddr_in))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in *p = (struct sockaddr_in *) in;
...
}
break;
case AF_INET6:
if(len < sizeof(struct sockaddr_in6))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in6 *p = (struct sockaddr_in6 *) in;
...
}
break;
[...other cases...]
default:
errno = EINVAL; // family not supported
return -1;
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,该函数可以检查len参数以确保长度足以满足预期的结构,因此它们可以reinterpret_cast<>()(就像在C++中调用的那样)指针.结构中的数据是否正确取决于调用者.在这方面没有太多选择.这些函数应该在使用数据之前验证所有类型的东西,并errno在发现问题时返回-1 .
因此,实际上,您有一个struct sockaddr_in或struct sockaddr_in6那个(重新解释)强制转换为a struct sockaddr和bind()函数(和其他人)将该指针强制转换为a struct sockaddr_in或者struct sockaddr_in6在检查sa_family成员并验证大小之后.