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

sas*_*alm 39 c sockets linux

绑定()函数接受一个指针sockaddr,但在我看到所有的实施例中,sockaddr_in结构来代替,并且被转换为sockaddr:

struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...
Run Code Online (Sandbox Code Playgroud)

我无法理解为什么使用sockaddr_in结构.为什么不准备并通过sockaddr

这只是惯例吗?

小智 51

不,这不仅仅是惯例.

sockaddr是任何类型的套接字操作的通用描述符,而是sockaddr_in特定于基于IP的通信的结构(IIRC,"in"代表"InterNet").据我所知,这是一种"多态":bind()函数假装采取a struct sockaddr *,但事实上,它会假设传递适当类型的结构; 即一个对应于套接字类型的套接字,作为第一个参数.

  • 只是添加:`sockaddr_in6`用于IPv6地址,`sockaddr_un`用于Unix域套接字,... (6认同)

Mih*_*hir 18

我不知道它是否与这个问题非常相关,但我想提供一些额外的信息,这可能会使类型等级更容易理解,因为许多没有花太多时间的人C看到这样的类型等级会感到困惑。

我使用macOS,所以我根据系统中的头文件来举例。

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) */
};
Run Code Online (Sandbox Code Playgroud)

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];
};
Run Code Online (Sandbox Code Playgroud)

从最基本的开始,指针只包含一个地址。所以struct sockaddr *struct sockaddr_in *几乎相同。他们都只存储一个地址。唯一相关的区别是编译器如何处理它们的对象。

所以当你说 时(struct sockaddr *) &name,你只是在欺骗编译器并告诉它这个地址指向一个struct sockaddr类型。


所以假设指针指向一个 location 1000。如果struct sockaddr *存储此地址,它将根据结构定义考虑从内存1000sizeof(struct sockaddr)拥有成员。如果struct sockaddr_in *存储相同的地址,它将考虑从1000to 的内存sizeof(struct sockaddr_in)


当您对该指针进行类型转换时,它将考虑相同的字节序列直到sizeof(struct sockaddr)

struct sockaddr *a = &name; // consider &name = 1000
Run Code Online (Sandbox Code Playgroud)

现在,如果我的访问a->sa_len,编译器会从一个地方访问1000sizeof(__uint8_t)它是相同的字节大小的情况下sockaddr_in。所以这应该访问相同的字节序列。

相同的模式适用于sa_family.

之后有一个 14 字节的字符数组,struct sockaddr其中存储来自in_port_t sin_port( typedef'd 16 位无符号整数 = 2 字节)、struct in_addr sin_addr(简单的 32 位 ipv4 地址 = 4 字节) 和char sin_zero[8](8 字节) 的数据。这 3 个加起来是 14 个字节。

现在这三个存储在这个 14 字节的字符数组中,我们可以通过访问适当的索引并再次对它们进行类型转换来访问这三个中的任何一个。

user529758 的回答已经解释了这样做的原因。


Mag*_*tel 5

这是因为bind可以绑定IP套接字以外的其他类型的套接字,例如Unix域套接字,其类型为sockaddr_un。AF_INET套接字的地址将主机和端口作为其地址,而AF_UNIX套接字的文件系统路径。