使用sockaddr_storage的API

时间:2012-01-12 12:46:32

标签: sockets winsock2

我正在尝试进行一些与IP无关的编码,并且根据各种来源的建议,我尝试使用sockaddr_storage。但是,所有API调用(getaddrinfo,getnameinfo)仍然依赖于struct sockaddr。他们之间的铸造并不是一个很好的选择,gves会遇到很多其他问题。

分别对sockaddr_in和sockaddr_in6进行投掷会使我试图使用sockaddr_storage失败。

在开发简单的客户端服务器套接字应用程序时有效使用sockaddr_storage的任何人。

2 个答案:

答案 0 :(得分:25)

联合进行IPV6和IPV4编程的问题在于,纯sockaddr结构本身不足以容纳sockaddr_in6。因此,如果您需要盲目传递一个可能是sockaddr_in或sockaddr_in6的地址,sockaddr_storage更容易使用。

在一天结束时,无论您使用的是sockaddr_in,sockaddr_in6还是sockaddr_storage,您都必须转发这些指针以调用sendto,recvfrom,connect,accept和许多其他套接字函数。它只是套接字编程的一个细微差别。放开做不安全事情的感觉。你的代码没问题。

现在,在编写适用于IPV4和IPV6的网络代码时,您可以轻松陷入拥有大量switch语句来处理不同网络类型的陷阱。然后代码变得如下:

if (addr.ss_family == AF_INET)
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in))
else (addr.ss_family == AF_INET6)
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in6));

然后那种类型的“if family == AF_INET”表达式可以很容易地反复重复。这就是你想要避免的。

假设您正在使用C ++,您会发现套接字地址对象的抽象类非常有用。我在github herehere上有一个例子。 CSocketAddress类由{sockaddr,sockaddr_in,sockaddr_in6}的联合支持,并且可以使用sockaddr_storage构造。如果我在开始这个课之前就已经知道了sockaddr_storage,我会用它而不是联合的东西。在任何情况下,它允许我编写如下代码:

CSocketAddress addr;
...
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength());

同样,“accept”语句如下所示:

sockaddr_storage addrstorage = {};
int len = sizeof(sockaddr_storage);
accept(sock, (sockaddr*)&addrstorage, &len);

CSocketAdddress addr(addrstorage); // construct an address object to pass around everywhere else

这对调用bind,send和recv的代码路径非常有帮助。现在,我的STUN服务器和客户端代码路径不再需要知道有关套接字地址的族类型的任何信息。他们只使用“CSocketAddress”对象。唯一的IPV4和IPV6特定代码是在客户端和服务器初始化期间 - 实际构造地址对象时。幸运的是,这也被部分抽象出来了。

您可能还想仔细阅读辅助函数here。还有一些更有用的东西,用于以IP无关的方式解析主机名,枚举适配器等。它是Linux代码,但有些应该映射到Windows和winsock。

我差不多为这个代码库添加了TCP支持。在添加对SOCK_STREAM的支持的过程中,我不必进行单个更改,也不必添加任何新代码来处理IPV4和IPV6地址结构的差异。

答案 1 :(得分:3)

我一般不认为需要struct sockaddr_storage。它的目的是为任何给定协议的sockaddr结构分配足够的空间,但是在IP版本无关的代码中你需要经常这样做吗?一般情况下,您致电getaddrinfo(),它会为您提供一堆struct sockaddr *,而您不关心它们是sockaddr_in还是sockaddr_in6,您只需将它们传递给 - 是bind()connect()(无需投射)。

在典型的客户端/服务器代码中,我可以想到struct sockaddr_storage有用的主要位置是为第二个参数预留空间accept()。在这种情况下,我同意将struct sockaddr *转换为accept()一次,getnameinfo()转换为{{1}}。但我无法看到围绕这些演员阵容的方法。这是C.结构继承总是涉及很多演员表。