IPv6连接错误WSAEAFNOSUPPORT

时间:2018-04-06 20:47:09

标签: c++ windows sockets winsock2

我使用主机名解析器为IPv4 / IPv6编写小型客户端。 对于IPv4和解析器,它很好,但是当connect()时没有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;
}

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

2 个答案:

答案 0 :(得分:2)

问题在于:

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结构进行零初始化也是一个好主意,因为它有更多的字段而不是您正在填写。

答案 1 :(得分:0)

您没有正确使用getaddrinfo()

首先,getaddrinfo()会返回您忽略的错误代码。

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

但无论如何,给定的主机名可能有多个与之关联的IP,并且可能无法从您的位置访问所有主机名,因此您应connect()对列表中的每个地址进行操作,一次一个或同时一个,直到其中一个成功。

此外,在这种情况下根本没有必要使用inet_pton()(你没有正确使用,正如@ rustyx&#39的回答所解释的那样)。 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;
}