52

我知道 sockaddr_in 用于 IPv4, sockaddr_in6 用于 IPv6。让我感到困惑的是 sockaddr 和 sockaddr_in[6] 之间的区别。

有些函数接受sockaddr,有些函数接受sockaddr_inor sockaddr_in6,所以:

  • 规则是什么?
  • 为什么需要两种不同的结构?

而且因为sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in).

  • 这是否意味着如果我们需要支持 ipv4 和 ipv6,我们应该始终使用 sockaddr_in6 在堆栈中分配内存并强制转换为 sockaddr 和 sockaddr_in?

一个例子是:我们有一个套接字,我们想获取它的字符串 ip 地址(可以是 ipv4 或 ipv6)。

我们首先调用getsockname获取一个addr,然后inet_ntop基于调用addr.sa_family

这个代码片段有什么问题吗?

char ipStr[256];
sockaddr_in6 addr_inv6;
sockaddr* addr = (sockaddr*)&addr_inv6;
sockaddr_in* addr_in = (sockaddr_in*)&addr_inv6;

socklen_t len = sizeof(addr_inv6);
getsockname(_socket, addr, &len);

if (addr->sa_family == AF_INET6) {
    inet_ntop(addr_inv6.sin6_family, &addr_inv6.sin6_addr, ipStr, sizeof(ipStr)); 
    // <<<<<<<<IS THIS LINE VALID, getsockname expected a sockaddr, but we use 
    // it output parameter as sockaddr_in6.
} else {
    inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipStr, sizeof(ipStr));
}
4

2 回答 2

30

sockaddr_in并且sockaddr_in6都是第一个成员是结构的sockaddr结构。

根据 C 标准,结构的地址和它的第一个成员是相同的,因此您可以将指针转换sockaddr_in(6)为指向的指针sockaddr

作为参数的函数sockaddr_in(6)可以修改该sockaddr部分,而sockaddr作为参数的函数只关心该部分。

这有点像继承。

于 2013-09-04T10:15:46.373 回答
25

我不想回答我的问题。但是为了在这里提供更多可能对其他人有用的信息,我决定回答我的问题。

在深入研究linux. 以下是我的发现,可能有多个协议都实现了getsockname. 并且每个都有自己的底层地址数据结构,例如,对于 IPv4 它是sockaddr_in,而对于 IPV6 sockaddr_in6sockaddr_un对于AF_UNIX套接字。sockaddr被用作这些 API 签名中的公共数据支柱。

这些 API 将根据 memcpy 的另一个参数将 socketaddr_in 或 sockaddr_in6 或 sockaddr_un 复制到 sockaddr length

并且所有数据结构都以相同类型的字段 sa_family 开头。

基于这些原因,代码片段是有效的,因为两者sockaddr_in都有sockaddr_in6sa_family然后我们可以将其转换为正确的数据结构,以便在 check 后使用sa_family

BTY,我不确定为什么sizeof(sockaddr_in6) > sizeof(sockaddr)会导致基于 sockaddr 的大小分配内存对于 ipv6 来说是不够的(这很容易出错),但我想这是因为历史原因。

于 2013-09-05T02:51:43.407 回答