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, &amp;hints, &amp;res);,我会在 res 中得到结果,然后我可以这样使用 c 样式:struct sockaddr_in *mytest = (struct sockaddr_in *)res-&gt;ai_addr; .它工作正常。但如果我使用 c++ 风格 static_cast 则它不起作用:struct sockaddr_in *mytest = static_cast&lt;struct sockaddr_in *&gt;(res-&gt;ai_addr);。我不确定 C 内部使用的是什么类型的转换。但从示例中可以明显看出它不是 static_caststatic_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 之间的转换的主要内容,如果未能解决你的问题,请参考以下文章

sockaddr和sockaddr_in的区别

struct sockaddr和struct sockaddr_in

sockaddr和sockaddr_in详解

sockaddr和sockaddr_in详解

socket编程 ------ sockaddr_in 和 sockaddr 的区别

sockaddr和sockaddr_in的区别