我知道sockaddr_in用于IPv4,sockaddr_in6用于IPv6。我的困惑是sockaddr和sockaddr_in之间的区别[6]。
有些功能接受sockaddr
,有些功能会接受sockaddr_in
或sockaddr_in6
,所以:
因为sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(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));
}
答案 0 :(得分:25)
sockaddr_in
和sockaddr_in6
都是第一个成员为sockaddr
结构的结构。
根据C标准,结构的地址及其第一个成员是相同的,因此您可以将指针转换为指向sockaddr_in(6)
的{{1}}。
以sockaddr
为参数的函数可能会修改sockaddr_in(6)
部分,而以sockaddr
为参数的函数只关心该部分。
这有点像继承。
答案 1 :(得分:20)
我不想回答我的问题。但是为了提供更多可能对其他人有用的信息,我决定回答我的问题。
深入研究linux
的源代码。以下是我的发现,可能有多个协议都实现了getsockname
。并且每个都有自己的地址数据结构,例如,对于IPv4,它是sockaddr_in
,IPV6 sockaddr_in6
和sockaddr_un
用于AF_UNIX
套接字。 sockaddr
用作这些API签名中的公共数据支柱。
那些API会通过memcpy将socketaddr_in或sockaddr_in6或sockaddr_un复制到另一个参数length
的sockaddr上。
并且所有数据结构都以相同的类型字段sa_family开头。
基于这些原因,代码段有效,因为sockaddr_in
和sockaddr_in6
都有sa_family
,然后我们可以将它转换为正确的数据结构,以便在检查后使用{{ 1}}。
BTY,我不知道为什么导致基于sockaddr大小分配内存的sa_family
对于ipv6来说还不够(这很容易出错),但我想这是因为历史原因。