我使用一个库来完成一些网络事务,当客户端连接时,这个库提供了一个“sockaddr *”,它保存了客户端套接字。我只是想提取IP和端口,目前我是这样做的:
#include <sys/socket.h>
#include <netdb.h>
const std::string Client::prepareIPandPort(struct sockaddr *hostaddr) {
assert(hostaddr != nullptr);
std::string ipport;
char clienthost[NI_MAXHOST];
char clientport[NI_MAXSERV];
int result = getnameinfo(hostaddr, sizeof(*hostaddr),
clienthost, sizeof(clienthost),
clientport, sizeof(clientport),
NI_NUMERICHOST | NI_NUMERICSERV);
if (result != 0) {
std::cerr << "Error: " << gai_strerror(result) << std::endl;
ipport = "unknown";
} else {
switch (hostaddr->sa_family) {
case AF_INET:
ipport = std::string {clienthost} + ":"
+ std::string {clientport};
break;
case AF_INET6:
ipport = "[" + std::string {clienthost} + "]:"
+ std::string {clientport};
break;
default:
ipport = "unknown";
}
}
return ipport;
}
在我的Mac上使用IPv4时,它可以工作。如果我在Gentoo Linux服务器上使用这个完全支持IPv6的应用程序,我只能得到:
错误:不支持ai_family
连接客户端具有AAAA和IP6记录。
我添加了一些couts并打印了hostaddr->sa_ some,它返回10,即AF_INET6。
为什么这不起作用?
发布于 2016-06-30 21:23:59
您不能使用sizeof(*hostaddr)
,因为hostaddr
是一个通用的sockaddr*
指针。不同的地址家族使用不同的sockaddr_...
类型,这些类型与sockaddr
本身的大小并不完全相同。getnameinfo()
需要根据其地址系列知道hostaddr
实际指向的sockaddr_...
结构的真正大小。
根据Linux documentation
EAI_FAMILY 地址族无法识别,或地址长度对于指定的家族无效。
sockaddr_in
( IPv4 )与sockaddr
的大小相同,这就是getnameinfo()
为IPv4“工作”的原因。但是sockaddr_in6
(IPv6)比sockaddr
大,这就是getnameinfo()
失败的原因。
最好的解决方案是让调用方通过正确的大小:
const std::string Client::prepareIPandPort(struct sockaddr *hostaddr, int hostaddrlen) {
...
int result = getnameinfo(hostaddr, hostaddrlen, ...);
...
}
sockaddr_in ipv4host;
...
client.prepareIPandPort((sockaddr*)&ipv4host, sizeof(ipv4host));
sockaddr_in6 ipv6host;
...
client.prepareIPandPort((sockaddr*)&ipv6host, sizeof(ipv6host));
否则,您必须计算它:
const std::string Client::prepareIPandPort(struct sockaddr *hostaddr) {
...
int hostaddrlen;
switch (hostaddr->sa_family) {
case AF_INET:
hostaddrlen = sizeof(sockaddr_in);
break;
case AF_INET6:
hostaddrlen = sizeof(sockaddr_in6);
break;
default:
std::cerr << "Error: " << gai_strerror(EAI_FAMILY) << std::endl;
return "unknown";
}
int result = getnameinfo(hostaddr, hostaddrlen, ...);
...
}
https://stackoverflow.com/questions/38132577
复制相似问题