sockaddr 和 sockaddr_in 之间的转换
Posted
技术标签:
【中文标题】sockaddr 和 sockaddr_in 之间的转换【英文标题】:casting between sockaddr and sockaddr_in 【发布时间】:2015-10-24 14:24:22 【问题描述】:我遇到了一个引用它的套接字编程教程
“指向 struct sockaddr_in 的指针可以转换为指向 struct sockaddr 的指针反之亦然”
我不明白如何将 sockaddr_in 转换为 sockaddr。将 Big 类型的指针转换为 Small 类型应该会产生 UD 行为。
struct sockaddr
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
;
struct sockaddr_in
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
;
演员阵容如何不被定义?将它们相互投射会不会不安全?
如果我有一个只有两个整数的 A 类和有 4 个整数的 B 类。如果我有一个 B 类型的指针并且我将它转换为 A 类型,那么确保我可以获取前两个元素。但是,如果 A 类首先声明了 2 个字符,然后声明了 2 个整数,那么指针将无法正确获取值,因为在这种情况下对象布局会有所不同。
编辑 1:
class Anu
public:
char a;
int b;
Anu()
a='a';
;
class Anurag
public:
Anurag() a=4;
int a;
int b;
int c;
int d;
;
int main()
Anu objanu;
Anurag objanurag;
Anurag *ptrAnurag= &objanurag;
ptrAnurag= (Anurag*)&objanu;
cout<<ptrAnurag->a; //Some weird value here
return 0;
假设我通过调整变量类型更改示例以使两个类具有相同的大小...即使大小保持不变,对象布局仍然可能不同。
【问题讨论】:
它们的大小有何不同? sockaddr: 2+14=16, sockaddr_in:2+2+4+8=16 是的,你是对的。但是 sockaddr 和 sockaddr_in 的对象布局会有所不同,即使大小看起来相似。将存储在 sockaddr 中的第一件事是代表 sa_family 的 2 个字节。然而,在 sockaddr_in 对象中,首先存储的是一个 4 字节的 int,表示 sin_family。如果 sockaddr_in 类型指针指向 sockaddr,则结果将是 UD,反之亦然。 不不,但 sin_family 不是 'int' 它是 'short int' 感谢 c++ 人员,但我只使用 'short' 来缩短问题 :) sin_port vs sa_data 类型呢? 【参考方案1】:我会在@gsamaras 的回答中补充说未定义的行为并不总是意味着坏事即将发生。未定义的行为实际上是说“我们*没有提供任何关于如果发生 XYZ 时接下来会发生什么的规范”。
(*C++ 标准)。
这是操作系统发生并说“它由我们定义”的地方。
尽管强制转换不相关的结构(sockaddr_in
,sockaddr
)可能是标准未定义的行为,但 OS API 指定它对其 API 有效。
【讨论】:
感谢您提供更笼统的方面,这是我的赞成票。 @David:你能看看我的 Edit1 部分问题【参考方案2】:它们大小相等,所以不,你没有得到任何UB!
证明:
#include <stdio.h>
struct sockaddr
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
;
// src: http://www.gta.ufrj.br/ensino/eel878/sockets/sockaddr_inman.html
struct in_addr
unsigned long s_addr; // load with inet_aton()
;
struct sockaddr_in
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
;
int main (void)
printf("%zu\n", sizeof(unsigned short) + sizeof(char) * 14);
printf("%zu\n", sizeof(short int) + sizeof(unsigned short int) + sizeof(struct in_addr) + sizeof(unsigned char) * 8);
return 0;
输出:
16
16
好评:sockaddr: 2+14=16, sockaddr_in:2+2+4+8=16 – Amer Agovic
您可能还想看看这个问题:Why isn't sizeof for a struct equal to the sum of sizeof of each member?
请检查这个问题:Is it possible to cast struct to another?
我也在这里复制 Benoit 的答案:
这被称为Type Punning。在这里,两个结构的大小相同,因此不存在结构大小的问题。尽管您几乎可以将任何东西投射到任何东西上,但使用结构做这件事很容易出错。
这也是理查德 J. 罗斯三世的:
这是 C 的“继承”形式(注意引号)。这是可行的,因为 C 不关心地址中的底层数据,只关心您将其表示为什么。
该函数通过使用 sa_family 字段确定它实际上是什么结构,并将其转换为函数内部的适当 sockaddr_in。
【讨论】:
是的,你是对的。但是 sockaddr 和 sockaddr_in 的对象布局会有所不同,即使大小看起来相似。将存储在 sockaddr 中的第一件事是代表 sa_family 的 2 个字节。然而,在 sockaddr_in 对象中,首先存储的是一个 4 字节的 int,表示 sin_family。如果 sockaddr_in 类型指针指向 sockaddr,则结果将是 UD,反之亦然。 地址转换为struct sockaddr *
的基本结构的大小无关紧要 - 请参阅struct sockaddr_un
,即 110 字节。出于这个原因,每个采用struct sockaddr *
的函数也采用socklen_t
。见man7.org/linux/man-pages/man7/unix.7.html
@gsamaras:谢谢你的答案。您能否看一下我的 Edit1 部分问题。事情对我来说还不是很清楚。
@gsamaras:如果我这样 getaddinfo :getaddrinfo(myhost, myport, &hints, &res);
,我会在 res 中得到结果,然后我可以这样使用 c 样式:struct sockaddr_in *mytest = (struct sockaddr_in *)res->ai_addr;
.它工作正常。但如果我使用 c++ 风格 static_cast
则它不起作用:struct sockaddr_in *mytest = static_cast<struct sockaddr_in *>(res->ai_addr);
。我不确定 C 内部使用的是什么类型的转换。但从示例中可以明显看出它不是 static_cast
。 static_cast
不应该工作吗?
在这种情况下,它们具有“相同大小”(或不同)的事实绝对没有任何意义。 C 在这里也没有帮助:行为在 C 中未定义,就像在 C++ 中未定义一样。【参考方案3】:
不同的大小无关紧要。就像您可以将不同长度的字符串传递给各种字符串处理函数一样,您也可以将不同长度的struct sockaddr
传递给各种套接字处理函数。
struct sockaddr
的大小由被调用函数根据结构的sa_family
成员的内容进行解释。另请注意,所有采用struct sockaddr *
地址的函数也采用socklen_t
参数,该参数保存所传递结构的大小。
例如struct sockaddr_un
结构为110字节:
struct sockaddr_un
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
;
bind()
或getpeername()
等被调用函数的声明类似于
int getpeerame(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
因为各种套接字结构的大小各不相同。
请注意,每个struct sockaddr_???
的第一个成员是sa_family
。因此它总是在同一个地方。
【讨论】:
以上是关于sockaddr 和 sockaddr_in 之间的转换的主要内容,如果未能解决你的问题,请参考以下文章
struct sockaddr和struct sockaddr_in