简体   繁体   English

为什么我们在调用 bind() 时将 sockaddr_in 转换为 sockaddr?

[英]Why do we cast sockaddr_in to sockaddr when calling bind()?

The bind() function accepts a pointer to a sockaddr , but in all examples I've seen, a sockaddr_in structure is used instead, and is cast to sockaddr : bind()函数接受一个指向sockaddr的指针,但在我见过的所有示例中,都使用了sockaddr_in结构,并将其sockaddrsockaddr

struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...

I can't wrap my head around why is a sockaddr_in struct used.我无法sockaddr_in为什么使用sockaddr_in结构。 Why not just prepare and pass a sockaddr ?为什么不准备并通过sockaddr

Is it just convention?只是约定俗成吗?

No, it's not just convention.不,这不仅仅是约定俗成。

sockaddr is a generic descriptor for any kind of socket operation, whereas sockaddr_in is a struct specific to IP-based communication (IIRC, "in" stands for "InterNet"). sockaddr是任何类型的套接字操作的通用描述符,而sockaddr_in是特定于基于 IP 的通信的结构(IIRC,“in”代表“InterNet”)。 As far as I know, this is a kind of "polymorphism" : the bind() function pretends to take a struct sockaddr * , but in fact, it will assume that the appropriate type of structure is passed in;据我所知,这是一种“多态性”: bind()函数假装采用struct sockaddr * ,但实际上,它会假设传入了适当类型的结构; ie one that corresponds to the type of socket you give it as the first argument.即对应于您将其作为第一个参数提供的套接字类型。

I don't know if its very much relevant for this question, but I would like to provide some extra info which may make the typecaste more understandable as many people who haven't spent much time with C get confused seeing such a typecaste.我不知道它是否与这个问题非常相关,但我想提供一些额外的信息,这可能会使类型等级更容易理解,因为许多没有花太多时间在C看到这样的类型等级会感到困惑。

I use macOS , so I am taking examples based on header files from my system.我使用macOS ,所以我根据系统中的头文件来举例。

struct sockaddr is defined as follows: struct sockaddr定义如下:

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) */
};

struct sockaddr_in is defined as follows: struct sockaddr_in定义如下:

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];
};

Starting from the very basics, a pointer just contains an address.从最基本的开始,指针只包含一个地址。 So struct sockaddr * and struct sockaddr_in * are pretty much the same.所以struct sockaddr *struct sockaddr_in *几乎相同。 They both just store an address.他们都只存储一个地址。 Only relevant difference is how compiler treats their objects.唯一相关的区别是编译器如何处理它们的对象。

So when you say (struct sockaddr *) &name , you are just tricking the compiler and telling it that this address points to a struct sockaddr type.所以当你说(struct sockaddr *) &name ,你只是在欺骗编译器并告诉它这个地址指向一个struct sockaddr类型。


So let's say the pointer is pointing to a location 1000 .因此,假设指针指向位置1000 If the struct sockaddr * stores this address, it will consider memory from 1000 to sizeof(struct sockaddr) possessing the members as per the structure definition.如果struct sockaddr *存储此地址,它将考虑从1000sizeof(struct sockaddr)内存,这些内存拥有结构定义中的成员。 If struct sockaddr_in * stores the same address it will consider memory from 1000 to sizeof(struct sockaddr_in) .如果struct sockaddr_in *存储相同的地址,它将考虑从1000sizeof(struct sockaddr_in)内存。


When you typecasted that pointer, it will consider the same sequence of bytes upto sizeof(struct sockaddr) .当您对该指针进行类型转换时,它会考虑到sizeof(struct sockaddr)的相同字节序列。

struct sockaddr *a = &name; // consider &name = 1000

Now if I access a->sa_len , the compiler would access from location 1000 to sizeof(__uint8_t) which is same bytes size as in case of sockaddr_in .现在,如果我访问a->sa_len ,编译器将从位置1000访问sizeof(__uint8_t) ,这与sockaddr_in字节大小相同。 So this should access the same sequence of bytes.所以这应该访问相同的字节序列。

Same pattern is for sa_family .相同的模式适用于sa_family

After that there is a 14 byte character array in struct sockaddr which stores data from in_port_t sin_port ( typedef 'd 16 bit unsigned integer = 2 bytes ) , struct in_addr sin_addr (simply a 32 bit ipv4 address = 4 bytes) and char sin_zero[8] (8 bytes).之后, struct sockaddr中有一个 14 字节的字符数组,它存储来自in_port_t sin_porttypedef 'd 16 位无符号整数 = 2 字节)、 struct in_addr sin_addr (简单的 32 位 ipv4 地址 = 4 字节)和char sin_zero[8] (8 个字节)。 These 3 add up to make 14 bytes.这 3 个加起来就是 14 个字节。

Now these three are stored in this 14 bytes character array and we can access any of these three by accessing appropriate indices and typecasting them again.现在这三个存储在这个 14 字节的字符数组中,我们可以通过访问适当的索引并再次对它们进行类型转换来访问这三个中的任何一个。

user529758's answer already explains the reason to do this. user529758 的回答已经解释了这样做的原因。

This is because bind can bind other types of sockets than IP sockets, for instance Unix domain sockets, which have sockaddr_un as their type.这是因为 bind 可以绑定除 IP 套接字之外的其他类型的套接字,例如 Unix 域套接字,其类型为 sockaddr_un。 The address for an AF_INET socket has the host and port as their address, whereas an AF_UNIX socket has a filesystem path. AF_INET 套接字的地址以主机和端口作为其地址,而 AF_UNIX 套接字具有文件系统路径。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM