简体   繁体   English

使与IPv6或IPv4的联系变得容易吗?

[英]make the connaction to IPv6 or IPv4 easy?

I am create socket class but I want to make my Connect function dynamic and can connect to address(ipv4 or ipv6) use switch to make IPv test and connect to supported IPv just wan to ask if I am right or is there an easy way to make it to make IPv4 or IPv6? 我正在创建套接字类,但是我想使我的Connect函数动态化,并且可以连接到地址(ipv4或ipv6),使用开关进行IPv测试并连接到受支持的IPv,只是想问我是否正确或是否有简单的方法使它成为IPv4或IPv6?

bool Connect(short port,std::string addr,bool vlisten,HWND WindowHandle,WSADATA& wsaData,bool async)
    {
        if(!hSocket);
        {
            this->port = port;
            this->addr =addr;
            this->vlisten = vlisten;
            this->WindowHandle = WindowHandle;
            this->wsaData =wsaData;
            this->init = true;

            // Provide big enough buffer, ipv6 should be the biggest
            char ipstr[INET6_ADDRSTRLEN];
            char ipstr2[INET6_ADDRSTRLEN];

            struct sockaddr_in* sockaddr_ipv4;
            struct sockaddr_in6* sockaddr_ipv6;
            //struct sockaddr_in6* sockaddr_ipv6;
            if(WSAStartup(MAKEWORD(2,2),&wsaData) !=0)
            {
                throw runtime_error("Error WSAStartup:" + WSAGetLastError());
            }

            if((this->hSocket = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))== INVALID_SOCKET)
            {
                Close();
                throw runtime_error("Error init sockect:" + WSAGetLastError());
            }

            if(addr != "INADDR_ANY")
            {
                struct addrinfo *result = nullptr;
                getaddrinfo(addr.c_str(), nullptr, nullptr, &result);
                struct addrinfo *it;
                for (it = result; it != nullptr; it = it->ai_next)
                {
                    //sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
                    //addr = inet_ntoa(sockaddr_ipv4->sin_addr);
                    //if (addr != "0.0.0.0") break;
                    switch (it->ai_family) 
                    {
                    case AF_UNSPEC:
                        cout<<"Unspecified\n"<<endl;
                        break;
                    case AF_INET:
                        cout<<"AF_INET (IPv4)\n"<<endl;
                        sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
                        //printf("\tIPv4 address %s\n",
                        addr = inet_ntoa(sockaddr_ipv4->sin_addr);
                        /*if (addr != "0.0.0.0") break;*/
                        break;
                    case AF_INET6:
                        cout<<"AF_INET (IPv6)\n"<<endl;
                        sockaddr_ipv6 = reinterpret_cast<sockaddr_in6*>(it->ai_addr);
                        addr = inet_ntop(it->ai_family,sockaddr_ipv6,(PSTR)ipstr,sizeof(ipstr));
                        break;
                    case AF_NETBIOS:
                        cout<<"AF_NETBIOS (NetBIOS)\n"<<endl;
                        break;
                    default:
                        printf("Other %ld\n", it->ai_family);
                        break;

                    }
                }
                freeaddrinfo(result);
            }
        }
        SOCKADDR_IN sockAddrIn;
        memset(&sockAddrIn,0,sizeof(sockAddrIn));
        sockAddrIn.sin_port = htons(port);
        sockAddrIn.sin_family =  AF_INET;
        sockAddrIn.sin_addr.s_addr = (addr == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(addr.c_str()));

        if(vlisten && (bind(hSocket,reinterpret_cast<SOCKADDR*>(&sockAddrIn),sizeof(sockAddrIn))== SOCKET_ERROR))
        {
            Close();
            throw runtime_error("Error vlisten & bind: " + WSAGetLastError());
        }

        if(async && WindowHandle)
        {
            if(WSAAsyncSelect(hSocket,WindowHandle,WM_SOCKET,FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE|FD_ACCEPT) !=0)
            {
                Close();
                throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
            }

        }

        if(vlisten && (listen(hSocket,SOMAXCONN)== SOCKET_ERROR))
        {
            Close();
            throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
        }

        if(!vlisten && (connect(hSocket, reinterpret_cast<SOCKADDR*>(&sockAddrIn), sizeof(sockAddrIn)) == SOCKET_ERROR))
        {
            if(async && WindowHandle && (WSAGetLastError() != WSAEWOULDBLOCK))
            {
                Close();
                throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
            }
        }
    }

Your code has multiple issues: 您的代码有多个问题:

  • First, you correctly called getaddrinfo() , but then you completely threw away the results without using them. 首先,您正确地调用了getaddrinfo() ,但是随后完全丢弃了结果而不使用它们。
  • You called listen() but you appear to intend to make an outgoing connection; 您调用了listen()但您似乎打算建立传出连接; listen() is meant to listen for incoming connections. listen()用于侦听传入的连接。
  • Instead of using the information from getaddrinfo() you ignore it and assume IPv4 when filling out your sockaddr_in structure. 无需使用getaddrinfo()的信息,而是在填充sockaddr_in结构时将其忽略并采用IPv4。 This part of the code should just be scrapped. 代码的这一部分应该被废弃。
  • There's no need to explicitly check the returned address family. 无需显式检查返回的地址族。 You won't get any address families that the computer can't handle. 您将不会获得计算机无法处理的任何地址族。
  • You appear to be writing a single method which does more than one thing, ie both make outgoing connections and accept incoming connections. 您似乎正在编写一个单一的方法,该方法可以完成多项任务,即建立传出连接并接受传入连接。 Methods should only do one thing. 方法只能做一件事。

Let's go back to the beginning and get a minimal outgoing connection up. 让我们回到开头,并建立一个最小的传出连接。 I'm omitting anything here not directly related to creating the connection (eg the call to WSAAsyncSelect() and other stuff which belongs in separate methods anyway): 我在这里省略了与创建连接没有直接关系的任何内容(例如,对WSAAsyncSelect()的调用以及无论如何都属于单独方法的其他内容):

// Connect to a remote host, given its address and port number.
bool Connect(short port, std::string addr)
{
    struct addrinfo *result, *rp;

    // TODO: You passed us an integer port number. We need a C string.
    // Clean up this mess.
    char portstr[255];
    portstr = sprintf("%d", port);

    if (getaddrinfo(addr.c_str(), portstr, nullptr, &result)) {
        throw runtime_error("getaddrinfo: " + WSAGetLastError());
    }

    // A host can have multiple addresses. Try each of them in turn until
    // one succeeds. Typically this will try the IPv6 address first, if
    // one exists, then the IPv4 address. The OS controls this ordering
    // and you should not attempt to alter it. (RFC 6724)
    for (rp = result; rp != nullptr; rp = rp->ai_next) {
        this->hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);

        // Check socket creation failed; maybe harmless (e.g. computer
        // doesn't have IPv6 connectivity). Real errors will get thrown below.
        if (this->hSocket == -1)
            continue;

        if (connect(this->hSocket, rp->ai_addr, rp->ai_addrlen) != -1)
            break;  // Success

        close(this->hSocket);
    }

    if (rp == NULL) {  // No address succeeded
        throw runtime_error("connect: " + WSAGetLastError());
    }

    freeaddrinfo(result);

    // Now this->hSocket has an open socket connection. Enjoy!
}

The major thing to note is that getaddrinfo() handles all the heavy lifting for you. 需要注意的主要事情是getaddrinfo()为您处理了所有繁重的工作。 The structure it returns has all the information needed to create the connection; 它返回的结构具有创建连接所需的所有信息。 you only have to use it. 您只需要使用它。

If you want the connection information, such as address and family, you can copy those out of rp and store them somewhere before it goes out of scope. 如果需要连接信息(例如地址和家庭),可以将其复制到rp然后将其存储在超出范围的位置。

Writing the separate method to handle incoming connections is left as an exercise for the reader. 读者可以自己写一个单独的方法来处理传入的连接。 Your example code appears to be partly based on the sample on the MSDN page for getaddrinfo ; 您的示例代码似乎部分基于MSDN页面上的getaddrinfo样本; the Linux getaddrinfo manual page has much better examples (the sample code actually works with minimal change on Windows). Linux getaddrinfo手册页提供了更好的示例(示例代码实际上在Windows上只需很少的更改即可工作)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM