16

我用谷歌搜索了它,有些人说“与 struct sockaddr 保持相同的大小”。但是内核不会直接使用 sockaddr (对吧?)。使用时。内核会将其转换回原来的样子。那么为什么需要零填充呢?

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
};

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET, AF_INET6
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};

struct in_addr {
    unsigned long s_addr;          // load with inet_pton()
};
4

4 回答 4

13

我能找到的另外两条相关信息是

谈一段清除字节的代码

这是一个错误。我看到它偶尔发生。此错误可能会导致应用程序中出现未定义的行为。

随后进行了一些解释

大部分网络代码不使用sockaddr_in,它使用sockaddr。当您使用像 sendto 这样的函数时,您必须将 sockaddr_in 或您正在使用的任何地址显式转换为 sockaddr。sockaddr_in 与 sockaddr 大小相同,但在内部大小相同,因为有一点点改动。

那个hack是sin_zero。sockaddr_in 中有用数据的长度确实比 sockaddr 短。但是使用一个小缓冲区在 sockaddr_in 中填充了差异;该缓冲区是 sin_zero。

最后,可以在各个地方找到的信息

在某些架构上,不清除 sin_zero 不会导致任何问题。但在其他架构上可能会。规范要求它清除 sin_zero,因此如果您希望您的代码现在和将来没有错误,则必须这样做。

回答问题

为什么我们需要这个 8 字节填充?

和答案

Unix 网络编程第 3.2 章说,“POSIX 规范只需要结构中的三个成员:sin_family、sin_addr 和 sin_port。符合 POSIX 的实现可以定义额外的结构成员,这对于 Internet 套接字是正常的地址结构。几乎所有的实现都添加了 sin_zero 成员,以便所有套接字地址结构的大小至少为 16 字节。”

这有点像结构填充,将来可能会保留给额外的字段。正如评论的那样,你永远不会使用它。

这与第一个链接一致。清除字节告诉接收器“我们这边没有使用这些字节”。

于 2013-03-25T06:39:28.460 回答
3

由于 struct sockaddr_in 需要转换为 struct sockaddr 它必须保持相同的大小,sin_zero 是一个未使用的成员,其唯一目的是将结构填充到 16 个字节(这是 sock_addr 的大小)。此填充大小可能因地址族而异。例如;

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];     // For padding, to make it same size as struct sockaddr 
}; 

现在以具有不同结构成员的 Xerox NS 系列为例:

struct sockaddr_ns {
    u_short sns_family;        // Address family, AF_NS 
    struct ns_addr sns_addr;        // the 12-byte XNS address 
    char sns_zero[2];        // unused except for padding 
};
于 2013-08-25T06:15:01.383 回答
1

发生结构填充是因为结构的成员必须出现在正确的字节边界,为了实现这一点,编译器会放入填充字节(或位,如果位字段正在使用),以便结构成员出现在正确的位置。此外,结构的大小必须使得在结构数组中所有结构都在内存中正确对齐。

因此,可能需要忽略内存泄漏。

于 2013-03-25T06:43:04.460 回答
1

struct sockaddr是这个结构的抽象的、不完整的版本,只有家庭。 struct sockaddr_in是这个结构的 IPv4 版本。它只使用前 8 个字节。 struct sockaddr_in6是这种结构的 IPv6 版本,并且更大。填充允许较小的结构容纳此结构的最大变化,因此缓冲区不会过小。

当您将地址传递函数或系统调用时,额外的字节并不是必需的。但是检索地址时,您需要为结果提供结构地址。该结构必须是所有可能变化中最大的。如果不是——假设你提供了一个 IPv4 版本,但取回了一个 IPv6 地址——那么结果将超出结构并破坏内存中隔壁的任何内容。

为了避免这种内存损坏,大多数相关函数都将结构大小作为参数。但是现在,当您通过该 IPv4 版本及其太小的尺寸时,您最终会得到一个不完全填充的 IPv6 版本的结构。看家庭,你可以看到它是IPv6。但是,如果您将该结构转换为 IPv6 并尝试使用它,则内容是错误的,因为该结构太小而无法包含完整的有效数据。

填充较小的结构可以避免这些障碍,并避免任何相关的潜在安全问题。

于 2017-03-14T04:38:19.340 回答