在C
中,一种典型的bind
套接字方式是以下方式:
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
int port_number = 55555;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port_number);
int result = bind(server_socket_fd,(struct sockaddr *)&addr , sizeof(addr));
if(bind_result > 0)
{
//
}
我想知道为什么从sockaddr_in
到sockaddr
的转换有效,因为我找不到任何文件来说明为什么有效。
似乎每个人都这样做。
为什么类型转换在这里起作用?
我没有问我们为什么要投射它,here已得到回答。我在问它为什么起作用。
答案 0 :(得分:3)
允许将结构指针转换为其他结构指针,然后再转换回来。 C standard的6.3.2.3p7节对此进行了详细说明:
指向对象类型的指针可能会转换为指向对象类型的指针 不同的对象类型。如果结果指针不正确 如果为引用的类型对齐,则行为未定义。 否则,再次转换回时,结果应比较等于 。当指向对象的指针转换为 指向字符类型的指针,结果指向最低地址 对象的字节。结果的连续递增,直到 对象的大小,产生指向剩余字节的指针 对象。
以上段落中有关对齐的限制,请参见6.2.5p28部分:
指向void的指针应具有相同的表示形式和对齐方式 要求作为指向字符类型的指针。48)同样,指针 兼容类型的合格或不合格版本应具有 相同的表示和对齐要求。 所有指向的指针 结构类型应具有相同的表示形式和对齐方式 。所有指向联合类型的指针都应具有 彼此具有相同的表示和对齐要求。指针 其他类型不必具有相同的表示形式或对齐方式 要求。
大概是bind
函数知道它具有哪种套接字描述符,并将struct sockaddr *
转换回struct sockaddr_in *
。
答案 1 :(得分:3)
sockaddr
结构基本上只有一个字段,即地址族。接收此结构的代码可以使用此字段来确定结构的实际类型是什么。实际使用的所有结构也都以该字段为第一个字段,因此该值是确定的。
实现还使结构具有相同的填充大小,因此内存使用情况也是完全确定的。这使其正常工作。
例如,Microsoft在Visual Studio 2017中将sockaddr
结构定义为
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
sa_data
所有不同套接字地址结构的最大大小。
因此,可能发送的任何“子”结构中必须包含14个字节的数据,或多或少。
sockaddr_in
是
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
此处的端口和in_addr
共需要六个字节,因此使用8个字节的填充来保持大小与sockaddr
相同。
当然可以创建例如sockaddr_un
,将其地址族设置为sockaddr_in
,并且任何接收该结构的代码都将错误地放入并获得完全错误的值。
答案 2 :(得分:2)
sockaddr
类型旨在用于适合于低级编程的高质量编译器。在此类编译器上,将结构的地址强制转换为共享公共初始序列的其他结构类型的指针将产生一个指针,该指针可用于检查该公共初始序列的成员,至少直到发生以下情况之一为止:
代码通过派生指针以外的方式写入这些字段。
代码通过派生指针以外的其他方式形成指针,这些指针将用于写入这些字段。 [[编译器可能会通过后一个指针将其后的写操作重新排序,使其回到创建时的位置,即使该写操作“应该”在检查CIS成员的代码之后,但是编译器也无法对这种写操作进行重新排序,除非它可以证明它将执行]。
代码进入一个循环,其中将发生上述情况之一。
代码调用一个函数,其中发生以上情况之一。
从标准的角度来看,对上述结构的支持本质上是实现质量的问题。基本原理明确认识到“符合”实施的质量可能很差,甚至没有用。应当从这种角度看待对编译器的任何过分容忍,因为它们太原始了,无法处理上述简单情况(例如gcc和clang)。
答案 3 :(得分:1)
之所以起作用,是因为bind
函数仅使用了某些第一字段,这些字段显然在sockrt_addr
结构家族的所有元素中都是相同的。