【问题标题】:IPv6 connect error WSAEAFNOSUPPORTIPv6 连接错误 WSAEAFNOSUPPORT
【发布时间】:2018-09-16 22:53:26
【问题描述】:

我使用主机名解析器为 IPv4 / IPv6 编写了一个小型客户端。 对于 IPv4 和解析器,它很好,但在连接()时不是 IPv6 我有一个问题 WSAGetLastError()说 WSAEAFNOSUPPORT。

我将所有结构(AF_INET -> AF_INET6、SOCKADDR_IN -> SOCKADDR_IN6)切换到 IPv6 版本。

#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
    printf("Simple_Client IPv4 & IPv6\n\n");

    // Initiates Winsock
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2, 0), &WSAData);

    // Get Parameters IP/PORT and request
    std::string str_HOSTNAME = "mirror.neostrada.nl";
    int PORT = 21;

    // RESOLVE IP
    BOOL is_IPv6 = FALSE;
    std::string str_dest_ip = "";

    addrinfo hints = { 0 };
    hints.ai_flags = AI_ALL;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    addrinfo * pResult;
    getaddrinfo(str_HOSTNAME.c_str(), NULL, &hints, &pResult);

    if (pResult == NULL)
    {
        printf("pResult error\n");
        return -1;
    }

    if (pResult->ai_family == AF_INET)
    {
        printf("getaddrinfo = AF_INET (IPv4)\n");
        is_IPv6 = FALSE;
    }

    if (pResult->ai_family == AF_INET6)
    {
        printf("getaddrinfo = AF_INET6 (IPv6)\n");
        is_IPv6 = TRUE;
    }

    char str[128];
    memset(str, 0, sizeof(str));

    if (is_IPv6 == FALSE) // IPv4
    {
        if (inet_ntop(AF_INET, &(*((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr))), str, INET_ADDRSTRLEN))
            str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
        else
            printf("inet_ntop error\n");
    }

    if (is_IPv6 == TRUE) // IPv6
    {
        if (inet_ntop(AF_INET6, &(*((ULONG*)&(((sockaddr_in6 *)pResult->ai_addr)->sin6_addr))), str, INET6_ADDRSTRLEN))
            str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
    }

    printf("%s : %s | Port : %i\n", is_IPv6 ? "IPv6" : "IPv4", str_dest_ip.c_str(), PORT);

    // Connect to the HOSTNAME
    SOCKET sock;

    if (is_IPv6 == TRUE)
    {
        SOCKADDR_IN6 sin;
        sin.sin6_family = AF_INET6;

        if(inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin) != 1)
            printf("ERROR inet_pton %i\n", WSAGetLastError());

        sin.sin6_port = htons(PORT);
        sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
        if (sock == INVALID_SOCKET)
            return -2;

        if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
        {
            printf("Connect Success to %s | PORT : %i\n", str_dest_ip.c_str(), PORT);
        }
        else
        {
            printf("ERROR connect to %s | PORT : %i : %i\n", str_dest_ip.c_str(), PORT, WSAGetLastError());
            Sleep(10000);
            return -2;
        }
    }

    char buf[1024] = { 0 };

    int size_recv = recv(sock, buf, sizeof(buf), 0);

    printf("SIZE RECV = %i | DATA RECV = %s\n", size_recv, char_to_string(buf, size_recv).c_str());

    WSACleanup();
    getchar();
    return 0;
}

如果有人有想法,感谢阅读。

【问题讨论】:

    标签: c++ windows sockets winsock2


    【解决方案1】:

    问题出在这里:

    inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin)
    

    这会将 IPv6 地址写入 sin6_family 字段的顶部,从而破坏整个结构。

    应该是:

    inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin.sin6_addr)
    

    在开始时将整个 sin 结构初始化为零也是一个好主意,因为它的字段比您要填写的要多。

    【讨论】:

    • 感谢您的回答!
    【解决方案2】:

    你没有正确使用getaddrinfo()

    一方面,getaddrinfo() 返回您忽略的错误代码。

    另一方面,getaddrinfo() 返回一个链接列表,由于您使用了@987654324,该列表可能包含多个 IPv4 和/或 IPv6 混合地址@。如果您只对 IPv6 感兴趣,请将 hints.ai_family 设置为 AF_INET6 而不是 AF_UNSPEC

    但无论哪种方式,给定的主机名可能有多个与之关联的 IP,并且它们可能无法从您所在的位置全部访问,因此您应该 connect()'ing 到列表中的每个地址,一次一个或并行,直到其中一个成功。

    此外,在这种情况下根本不需要使用inet_pton()(正如@rustyx 的回答所解释的那样,您没有正确使用它)。 getaddrinfo() 返回完全填充的 sockaddr_in(6) 结构,您可以将其按原样传递给 connect()

    试试类似的方法:

    #include <winsock2.h>
    #include <ws2tcpip.h>
    
    #include <iostream>
    #include <string>
    
    #pragma comment(lib, "ws2_32.lib")
    
    std::string addr_to_str(addrinfo *addr)
    {
        char str[128];
    
        switch (addr->ai_family)
        {
            case AF_INET: // IPv4
            {
                if (inet_ntop(AF_INET, &(((sockaddr_in*)(addr->ai_addr))->sin_addr), str, INET_ADDRSTRLEN))
                    return str;
    
                ret = WSAGetLastError();
                break;
            }
    
            case AF_INET6: // IPv6
            {
                if (inet_ntop(AF_INET6, &(((sockaddr_in6*)(addr->ai_addr))->sin6_addr), str, INET6_ADDRSTRLEN))
                    return str;
    
                ret = WSAGetLastError();
                break;
            }
    
            default:
                ret = WSAEAFNOSUPPORT;
                break;
        }
    
        std::cerr << "inet_ntop error: " << ret << std::endl;
        return "";
    }
    
    int main()
    {
        std::cout << "Simple_Client IPv4 & IPv6" << std::endl << std::endl;
    
        // Initiates Winsock
        WSADATA WSAData;
        int ret = WSAStartup(MAKEWORD(2, 0), &WSAData);
        if (ret != 0)
        {
            std::cerr << "WSAStartup error: " << ret << std::endl;
            return -1;
        }
    
        // Get Parameters IP/PORT and request
        std::string str_HOSTNAME = "mirror.neostrada.nl";
        int PORT = 21;
    
        // RESOLVE IP    
        addrinfo hints = { 0 };
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
    
        addrinfo *pResult = NULL;
        ret = getaddrinfo(str_HOSTNAME.c_str(), std::to_string(PORT).c_str(), &hints, &pResult);
        if (ret != 0)
        {
            std::cerr << "getaddrinfo error: " << ret << std::endl;
            WSACleanup();
            return -1;
        }
    
        // Log the IPs
        bool has_IPv4 = false;
        bool has_IPv6 = false;
    
        for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
        {
            switch (addr->ai_family)
            {
                case AF_INET: // IPv4
                {
                    has_IPv4 = true;
                    std::cout << "IPv4 : " << addr_to_str(addr);
                    break;
                }
    
                case AF_INET6: // IPv6
                {
                    has_IPv6 = true;
                    std::cout << "IPv6 : " << addr_to_str(addr);
                    break;
                }
            }
        }
    
        // Connect to the HOSTNAME
        SOCKET sock = INVALID_SOCKET;
    
        if (has_IPv6)
        {
            // try IPv6 first...
            for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
            {
                if (addr->ai_family != AF_INET6)
                    continue;
    
                std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
    
                sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
                if (sock == INVALID_SOCKET)
                {
                    ret = WSAGetLastError();
                    std::cerr << "socket error: " << ret << std::endl;
                    continue;
                }
    
                if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
                {
                    ret = WSAGetLastError();
                    std::cerr << "connect error: " << ret << std::endl;
                    closesocket(sock);
                    sock = INVALID_SOCKET;
                    continue;
                }
    
                break;
            }
        }
    
        if ((sock == INVALID_SOCKET) && (has_IPv4))
        {
            // try IPv4 next...
            for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
            {
                if (addr->ai_family != AF_INET)
                    continue;
    
                std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
    
                sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
                if (sock == INVALID_SOCKET)
                {
                    ret = WSAGetLastError();
                    std::cerr << "socket error: " << ret << std::endl;
                    continue;
                }
    
                if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
                {
                    ret = WSAGetLastError();
                    std::cerr << "connect error: " << ret << std::endl;
                    closesocket(sock);
                    sock = INVALID_SOCKET;
                    continue;
                }
    
                break;
            }
        }
    
        freeaddrinfo(pResult);
    
        if (sock == INVALID_SOCKET)
        {
            WSACleanup();
            return -2;
        }
    
        std::cout << "Connect Successful" << std::endl;
    
        char buf[1024];
    
        int size_recv = recv(sock, buf, sizeof(buf), 0);
        if (size_recv == SOCKET_ERROR)
        {
            ret = WSAGetLastError();
            std::cerr << "recv error: " << ret << std::endl;
        }
        else
        {
            std::cout << "SIZE RECV = " << size_recv;
            if (size_recv > 0)
            {
                std::cout << " | DATA RECV = ";
                std::cout.write(buf, size_recv);
            }
            std::cout << std::endl;
        }
    
        closesocket(sock);
        WSACleanup();
    
        std::cin.get();
        return 0;
    }
    

    【讨论】:

    • 感谢您非常详细的回答!
    猜你喜欢
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2017-12-26
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多