首页 > 解决方案 > C 套接字双栈 ss_family 始终 IPv6

问题描述

使用accept()orgetpeername()时,sockaddr_storage总是有ss_family=AF_INET6

struct sockaddr_storage address = {0};
socklen_t sockaddrlen = sizeof(address);
int client = accept(sock, (struct sockaddr*)(&address), &sockaddrlen);
if (client < 0) {
    perror("Unable to accept");
    exit(EXIT_FAILURE);
}
if( address.ss_family==AF_INET6  ){
    std::cout << "IPv6" << std::endl;
} else {
    std::cout << "IPv4" << std::endl;
}

我觉得它与创作有关:

s = socket(AF_INET6, SOCK_STREAM, 0);

或绑定

struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port   = htons(port);
addr.sin6_addr   = in6addr_any;

if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {

我怎样才能得到ss_family更正,或以另一种方式告诉它是什么类型的 IP?

标签: csocketsnetworking

解决方案


双栈套接字是 IPv6 套接字,仅支持 IPv4,因此其 IP 地址将始终是AF_INET6地址。但是,对于 IPv4 连接,这些地址将是IPv4 映射的 IPv6 地址

混合双栈 IPv6/IPv4 实现识别一类特殊的地址,即 IPv4 映射的 IPv6 地址。这些地址通常使用标准 IPv6 格式的 96 位前缀编写,其余 32 位使用 IPv4 的习惯点十进制表示法编写。

该组中的地址由 80 位前缀的 0 组成,接下来的 16 位是 1,其余的最低有效 32 位包含 IPv4 地址。例如 ::ffff:192.0.2.128 表示 IPv4 地址 192.0.2.128。另一种格式,称为“与 IPv4 兼容的 IPv6 地址”,是 ::192.0.2.128;但是,此方法已被弃用。

您需要明确检测到,请参阅如何从 IPv4 映射的 IPv6 地址解析 IPv4 地址?, 例如 :

#ifndef IN6_IS_ADDR_V4MAPPED
#define IN6_IS_ADDR_V4MAPPED(a) \
    ((((a)->s6_words[0]) == 0) && \
    (((a)->s6_words[1]) == 0) && \
    (((a)->s6_word[2]) == 0) && \
    (((a)->s6_word[3]) == 0) && \
    (((a)->s6_word[4]) == 0) && \
    (((a)->s6_word[5]) == 0xFFFF))
#endif

struct sockaddr_storage address = {0};
socklen_t sockaddrlen = sizeof(address);
int client = accept(sock, (struct sockaddr*)(&address), &sockaddrlen);
if (client < 0) {
    perror("Unable to accept");
    exit(EXIT_FAILURE);
}
if (address.ss_family == AF_INET6){
    struct sockaddr_in6 *addr = (struct sockaddr_in6*)(&address);
    if (IN6_IS_ADDR_V4MAPPED(&(addr->sin6_addr))) {
        std::cout << "IPv4 (mapped)" << std::endl;
    } else {
        std::cout << "IPv6" << std::endl;
    }
} else {
    std::cout << "IPv4" << std::endl;
}

推荐阅读