作者: | 来源:互联网 | 2023-07-06 16:16
Imlookingatfunctionssuchasconnect()andbind()inCsocketsandnoticethattheytakeapoint
I'm looking at functions such as connect()
and bind()
in C sockets and notice that they take a pointer to a sockaddr
struct. I've been reading and to make your application AF-Independent, it is useful to use the sockaddr_storage
struct pointer and cast it to a sockaddr
pointer because of all the extra space it has for larger addresses.
我将查看在C套接字中连接()和bind()之类的函数,并注意到它们使用一个指向sockaddr结构的指针。我一直在阅读并使您的应用程序与af无关,使用sockaddr_storage struct指针并将其转换为sockaddr指针是很有用的,因为它有足够的空间用于更大的地址。
What I am wondering is how functions like connect()
and bind()
that ask for a sockaddr
pointer go about accessing the data from a pointer that points at a larger structure than the one it is expecting. Sure, you pass it the size of the structure you are providing it, but what is the actual syntax that the functions use to get the IP Address off the pointers to larger structures that you have cast to struct *sockaddr
?
我想知道的是,像connect()和bind()这样请求sockaddr指针的函数是如何从指向比预期的更大结构的指针访问数据的。当然,您将提供的结构的大小传递给它,但是函数用于从指针中获取IP地址的实际语法是什么?
It's probably because I come from OOP languages, but it seems like kind of a hack and a bit messy.
这可能是因为我来自OOP语言,但它看起来有点像一个黑客,有点混乱。
2 个解决方案
40
Functions that expect a pointer to struct sockaddr
probably typecast the pointer you send them to sockaddr
when you send them a pointer to struct sockaddr_storage
. In that way, they access it as if it was a struct sockaddr
.
函数期望一个指向struct sockaddr的指针,当你向sockaddr发送一个指向struct sockaddr_storage的指针时,它可能会将你发送给sockaddr的指针进行排版。通过这种方式,他们访问它就好像它是一个结构体sockaddr。
struct sockaddr_storage
is designed to fit in both a struct sockaddr_in
and struct sockaddr_in6
struct sockaddr_storage被设计成适合于struct sockaddr_in和struct sockaddr_in6。
You don't create your own struct sockaddr
, you usually create a struct sockaddr_in
or a struct sockaddr_in6
depending on what IP version you're using. In order to avoid trying to know what IP version you will be using, you can use a struct sockaddr_storage
which can hold either. This will in turn be typecasted to struct sockaddr
by the connect(), bind(), etc functions and accessed that way.
您不会创建自己的struct sockaddr,您通常会创建一个struct sockaddr_in或struct sockaddr_in6,这取决于您使用的IP版本。为了避免尝试知道将要使用的IP版本,您可以使用一个struct sockaddr_storage,它可以保存其中任何一个。然后通过connect()、bind()等函数将其类型设置为struct sockaddr,并通过这种方式进行访问。
You can see all of these structs below (the padding is implementation specific, for alignment purposes):
您可以在下面看到所有这些结构(为对齐目的,填充是特定于实现的):
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];
};
So as you can see, if the function expects an IPv4 address, it will just read the first 4 bytes (because it assumes the struct is of type struct sockaddr
. Otherwise it will read the full 16 bytes for IPv6).
如您所见,如果函数需要一个IPv4地址,它将只读取前4个字节(因为它假设结构体是struct sockaddr类型。否则它将读取IPv6的全部16字节)。
7
In C++ classes with at least one virtual function are given a TAG. That tag allows you to dynamic_cast<>()
to any of the classes your class derives from and vice versa. The TAG is what allows dynamic_cast<>()
to work. More or less, this can be a number or a string...
在c++类中,具有至少一个虚函数的类被赋予一个标记。该标记允许您将dynamic_cast<>()转换为类派生的任何类,反之亦然。这个标记允许dynamic_cast<>()工作。或多或少,这可以是一个数字或一个字符串…
In C we are limited to structures. However, structures can also be assigned a TAG. In fact, if you look at all the structures that theprole posted in his answer, you will notice that they all start with 2 bytes (an unsigned short) which represents what we call the family of the address. This defines exactly what the structure is and thus its size, fields, etc.
在C语言中,我们仅限于结构。但是,也可以为结构分配一个标记。事实上,如果你看一下theprole在他的回答中发布的所有结构,你会发现它们都以2个字节(一个无符号的短)开头,这表示我们所说的地址的族。它准确地定义了结构是什么,因此它的大小、字段等等。
Therefore you can do something like this:
因此你可以这样做:
int bind(int fd, struct sockaddr *in, socklen_t len)
{
switch(in->sa_family)
{
case AF_INET:
if(len
As you can see, the function can check the len
parameter to make sure that the length is enough to fit the expected structure and therefore they can reinterpret_cast<>()
(as it would be called in C++) your pointer. Whether the data is correct in the structure is up to the caller. There is not much choice on that end. These functions are expected to verify all sorts of things before it uses the data and return -1 and errno
whenever a problem is found.
如您所见,该函数可以检查len参数,以确保长度足以满足预期的结构,因此它们可以重新解释您的指针(在c++中,它将被称为)的t_cast<>()。结构中的数据是否正确取决于调用者。在这方面没有多少选择。这些函数将在使用数据之前验证所有的东西,并在发现问题时返回-1和errno。
So in effect, you have a struct sockaddr_in
or struct sockaddr_in6
that you (reinterpret) cast to a struct sockaddr
and the bind()
function (and others) cast that pointer back to a struct sockaddr_in
or struct sockaddr_in6
after they checked the sa_family
member and verified the size.
因此,实际上,您有一个struct sockaddr_in或struct sockaddr_in6,您(重新解释)将其转换为一个struct sockaddr, bind()函数(和其他函数)将指针转换回一个struct sockaddr_in或struct sockaddr_in6,在检查sa_family成员并验证大小之后。