实际上,我将IPv4服务器应用程序移植到Linux上的双栈IPv4 / IPv6应用程序。
我使用以下方法解决的基本功能:
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_addr = in6addr_any;
...
bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
...
listen(sock, 5);
...
newsock = accept(syn->sock, (struct sockaddr *) &cli_addr, &clilen);
我可以连接IPv4和IPv6并使用连接。但是,当我想获得IP时:
switch(data->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)data)->sin_addr), buffer, size);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)data)->sin6_addr), buffer, size);
break;
default:
buffer[0] = '?';
buffer[1] = 0;
}
我总是按预期获得IPv6地址,或者如果它是IPv4连接,例如:: ffff:127.0.0.1
我需要更改什么,以127.0.0.1的形式显示为普通的旧IPv4地址(不带:: ffff:-prefix)?
由于 玩具
答案 0 :(得分:1)
您可以采用两种方式:
从头开始删除此::ffff:
(如果存在),并将其作为IPv4的指示符。
使用两个不同的套接字。那里有OS不支持IPv4和6通过一个套接字(IOW,它们始终启用V6ONLY
)。特别是,WinXP就这样做了。
然后你只能做一件事:
getaddrinfo()
和AI_PRIVATE
获取您可以拥有的所有本地套接字地址V6ONLY
select()
等确定要调用accept()
中的哪一个。听起来比实际更复杂......
答案 1 :(得分:1)
您请求显示IPv4映射连接,就好像它们实际上是IPv4,代码如下所示:
int cli = accept( server, NULL, NULL );
int addrform = AF_INET;
setsockopt( cli, IPPROTO_IPV6, IPV6_ADDRFORM, &addrform, sizeof(addrform) );
这需要针对每个新的客户端套接字进行。 IPv6连接失败;您可以忽略它或先检查,只有在使用IPv4映射客户端时才调用它。
完成后,您可以拨打getpeername()
以首选格式获取客户地址信息,而不是将其作为accept()
调用的一部分。
答案 2 :(得分:1)
有一个宏可以帮助,但只有一半的战斗:
if (IN6_IS_ADDR_V4MAPPED(&serv_addr.sin6_addr)) {
struct sockaddr_in tmpsa;
tmpsa.sin_family = AF_INET;
tmpsa.sin_port = 0;
tmpsa.sin_addr.s_addr = serv_addr.sin6_addr.s6_addr32[3];
/* process IPv4 address in tmpsa ... */
inet_ntop (AF_INET, &tmpsa.sin_addr, buffer, size);
} else {
/* process IPv6 address in serv_addr.. */
inet_ntop (AF_INET6, &serv_addr.sin6_addr, buffer, size);
}
根据平台的不同,可能需要进行少量修改,这里使用的是GNU标准。