sockaddr,sockaddr_in和sockaddr_in6有什么区别?

问题描述 投票:40回答:2

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

有些函数接受sockaddr,有些函数接受sockaddr_insockaddr_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));
}
c++ c api network-programming
2个回答
25
投票

sockaddr_insockaddr_in6都是第一个成员是sockaddr结构的结构。

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

sockaddr_in(6)为参数的函数可以修改sockaddr部分,并且以sockaddr作为参数的函数关注该部分。

这有点像继承。


20
投票

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

在深入了解linux的源代码之后。以下是我的发现,有可能多个协议都实现getsockname。并且每个都有自己的地址数据结构,例如,对于IPv4,它是sockaddr_in,IPV6和sockaddr_in6,以及sockaddr_un用于AF_UNIX套接字。 sockaddr用作这些API签名中的公共数据支柱。

这些API会将socketaddr_in或sockaddr_in6或sockaddr_un复制到另一个参数length上的sockaddr,由memcpy。

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

基于这些原因,代码片段是有效的,因为sockaddr_insockaddr_in6都有sa_family,然后我们可以在检查sa_family之后将其强制转换为正确的数据结构。

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

© www.soinside.com 2019 - 2024. All rights reserved.