调用bind()时,为什么要将sockaddr_强制转换为sockaddr?
该函数接受指向调用bind()时,为什么要将sockaddr_强制转换为sockaddr?,c,linux,sockets,C,Linux,Sockets,该函数接受指向sockaddr的指针,但在我看到的所有示例中,都使用了结构中的sockaddr\u,并强制转换为sockaddr: struct sockaddr_in name; ... if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) ... struct sockaddr\u的名称; ... if(bind(sock,(struct sockaddr*)&name,sizeof(name)) 100
sockaddr
的指针,但在我看到的所有示例中,都使用了结构中的sockaddr\u,并强制转换为sockaddr
:
struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...
struct sockaddr\u的名称;
...
if(bind(sock,(struct sockaddr*)&name,sizeof(name))<0)
...
我不明白为什么要在
结构中使用sockaddr\u。为什么不准备并传递一个sockaddr
这只是惯例吗?不,这不仅仅是惯例
sockaddr
是任何类型套接字操作的通用描述符,而sockaddr\u in
是特定于基于IP的通信(IIRC,“in”代表“InterNet”)的结构。据我所知,这是一种“多态性”:函数bind()
假装接受一个struct sockaddr*
,但实际上,它会假定传入了适当类型的结构;我E与作为第一个参数提供的套接字类型相对应的套接字。这是因为bind可以绑定IP套接字以外的其他类型的套接字,例如Unix域套接字,其类型为sockaddr\u un。AF_INET套接字的地址以主机和端口作为其地址,而AF_UNIX套接字有一个文件系统路径。我不知道它是否与这个问题非常相关,但是我想提供一些额外的信息,这可能会使类型分类更容易理解,因为许多没有花太多时间在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) */
};
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];
};
中的结构sockaddr\u定义如下:
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 {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
从最基本的开始,指针只包含一个地址。因此*
中的struct sockaddr*
和struct sockaddr\u几乎是一样的。它们都只存储一个地址。唯一相关的区别是编译器如何处理它们的对象
因此,当你说(struct sockaddr*)&name
,你只是在欺骗编译器,告诉它这个地址指向一个struct sockaddr
类型
假设指针指向一个位置1000
。如果<代码>结构SOCKADDR*<代码>存储此地址,它将考虑从代码< > 1000代码>代码> >代码> SIEZOF(StultSoCKADDR)按照结构定义拥有成员。如果<代码>结构SOCKADDRIGIN *<代码>存储相同的地址,它将考虑内存从<代码> 1000代码>到<代码> siZeof(StutoSokAdDrILIN)
当您键入该指针时,它会考虑到相同的字节序列(<>代码> siZeof(StultSoCKADDR)
现在,如果我访问a->sau len
,编译器将从位置1000
访问sizeof(\uu uint8\u t)
,这与中的sockaddr\u的字节大小相同。所以这应该访问相同的字节序列
同样的模式适用于sa_系列
在这之后,struct sockaddr
中有一个14字节的字符数组,它将中的数据存储在\u-port\t sin\u-port
(typedef
'd 16位无符号整数=2字节)、struct in\u-addr sin\u-addr
(仅32位ipv4地址=4字节)和char sin-zero[8]
(8字节)。这3个加起来就是14个字节
现在这三个都存储在这个14字节字符数组中,我们可以通过访问适当的索引并再次键入来访问这三个中的任何一个
user529758的回答已经解释了这样做的原因。NBsockaddr\u in 6
也存在,任何编写新代码的人都应该明智地将其包括在内……您在代码中省略了一个非常重要的部分:name.sa\u family=AF\u INET
forstruct sockaddr\u in
!考虑<代码>结构SOCKADDR 是所有其他SoCKADDR类型的结合。唯一的共同点是,它们有一个第一个成员sau family\t sau family
,必须与实际的结构类型相对应。只需添加:sockaddr\u in6
用于IPv6地址,sockaddr\u un
用于Unix域套接字,@MartinR I也在考虑蓝牙(如果我没有弄错的话,Linux是通过套接字进行RFCOMM的)等等。有很多套接字类型我都不知道……值得一提的是struct sockaddr_storage
,它在某种意义上也是“通用的”,并且足够大,可以容纳任何类型的套接字地址。