11

特别是 sin_addr 似乎位于 IPv4 和 IPv6 套接字寻址的不同内存位置。这导致奇怪:

#include <stdio.h>                                                                               
#include <netinet/in.h>                                                                          

int main(int argc, char ** argv) {                                                               
  struct sockaddr_in sa;                                                                         
  printf("sin_addr in sockaddr_in  = %p\n", &sa.sin_addr);                                       
  printf("sin_addr in sockaddr_in6 = %p\n", &((struct sockaddr_in6*)&sa)->sin6_addr);            
};

输出:

sin_addr in sockaddr_in  = 0x7fffa26102b4
sin_addr in sockaddr_in6 = 0x7fffa26102b8

为什么这两个值不一样?

由于 this 指向相同的数据(要连接的地址),因此应该位于相同的地址。否则,您应该如何使用您不知道是 IPv4 或 IPv6 的 sockaddr_in 调用 inet_ntop ?

4

2 回答 2

28

为什么这两个值不一样?

sockaddr_in并且sockaddr_in6是用于不同地址族(分别为 IPv4 和 IPv6)的不同结构。它们不需要以任何方式相互兼容,除了一个 - 第一个字段必须是一个 16 位整数来保存地址族。 sockaddr_in始终将该字段设置为AF_INET,并且sockaddr_in6始终将该字段设置为AF_INET6。通过以这种方式标准化 family 字段,任何sockaddr基于 的 API 都可以访问该字段并知道如何根据需要解释其余的结构数据。这也是为什么sockaddr基于 - 的 API 通常也有一个int大小值作为输入/输出,因为sockaddr_insockaddr_in6是不同的字节大小,因此 API 需要能够验证您传递的任何缓冲区的大小。

由于 this 指向相同的数据(要连接的地址),因此应该位于相同的地址。

不,不应该。结构中地址字段的位置特定于结构所属的地址族的类型。没有要求sockaddr_in并且sockaddr_in6应该将它们的地址存储在完全相同的偏移量处。

否则,您应该如何使用您不知道是 IPv4 或 IPv6 的 sockaddr_in 调用 inet_ntop ?

sockaddr_in只能与 IPv4 一起使用,不能与其他任何东西sockaddr_in6一起使用,并且只能与 IPv6 和其他任何东西一起使用。如果你有 asockaddr_in那么你隐含地知道你有一个 IPv4 地址,如果你有 asockaddr_in6那么你隐含地知道你有一个 IPv6 地址。您必须指定该信息,inet_ntop()以便它知道如何解释您传递给它的数据:

struct sockaddr_in sa;
inet_ntop(AF_INET, &(sa.sin_addr), ...);

.

struct sockaddr_in6 sa;
inet_ntop(AF_INET6, &(sa.sin6_addr), ...);

为了帮助您编写与家庭无关的代码,您应该尽可能使用sockaddr_storagesockaddr_in直接sockaddr_in6使用。 sockaddr_storage大小足以容纳sockaddr_insockaddr_in6结构。由于两个结构都以相同的偏移量和大小定义了一个族字段,sockaddr_storage因此可以与任何对sockaddr*指针(connect()accept()bind()getsockname()getpeername()等)进行操作的 API 一起使用。

但是,inet_ntop()不属于该类别,因此您必须sockaddr_storage在使用时手动拆开 a inet_ntop(),例如:

struct sockaddr_storage sa;

switch (sa.ss_family)
{
case AF_INET:
    inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
    break;
case AF_INET6:
    inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
    break;
}
于 2012-10-31T21:46:13.507 回答
3

不,对于 ipv6,您需要使用

in6_addr // is used to store the 128-bit network address

sockaddr_in6

详情可以参考这里

要编写支持双栈的代码,即 ipv4 和 6,请使用

于 2012-10-31T11:50:28.710 回答