C++ 使到IPv6或IPv4的连接变得容易?

C++ 使到IPv6或IPv4的连接变得容易?,c++,windows,sockets,ipv6,ipv4,C++,Windows,Sockets,Ipv6,Ipv4,我是CreateSocket类,但我想使我的连接函数动态,并可以连接到地址(ipv4或ipv6)使用交换机进行IPv测试,并连接到支持的IPv只是wan,问我是否正确,或者是否有一种简单的方法使其成为ipv4或ipv6 bool Connect(short port,std::string addr,bool vlisten,HWND WindowHandle,WSADATA& wsaData,bool async) { if(!hSocket);

我是CreateSocket类,但我想使我的连接函数动态,并可以连接到地址(ipv4或ipv6)使用交换机进行IPv测试,并连接到支持的IPv只是wan,问我是否正确,或者是否有一种简单的方法使其成为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());
            }
        }
    }
bool-Connect(短端口,std::string-addr,bool-vlisten,HWND-WindowHandle,WSADATA&WSADATA,bool-async)
{
if(!hSocket);
{
此->端口=端口;
这个->地址=地址;
这->vlisten=vlisten;
此->窗口句柄=窗口句柄;
这->wsaData=wsaData;
这->init=true;
//提供足够大的缓冲区,ipv6应该是最大的
char ipstr[INET6_ADDRSTRLEN];
char ipstr2[INET6_ADDRSTRLEN];
*sockaddr_ipv4中的结构sockaddr_;
结构sockaddr_in6*sockaddr_ipv6;
//结构sockaddr_in6*sockaddr_ipv6;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
抛出运行时错误(“错误WSAStartup:+WSAGetLastError());
}
if((this->hSocket=::套接字(AF\u INET、SOCK\u STREAM、IPPROTO\u TCP))==无效的套接字)
{
Close();
抛出运行时_错误(“error init socket:+WSAGetLastError());
}
if(addr!=“INADDR_ANY”)
{
struct addrinfo*result=nullptr;
getaddrinfo(addr.c_str()、nullptr、nullptr和result);
结构addrinfo*it;
for(it=result;it!=nullptr;it=it->ai_next)
{
//sockaddr\u ipv4=重新解释强制转换(it->ai\u addr);
//addr=inet\u ntoa(sockaddr\u ipv4->sin\u addr);
//如果(地址!=“0.0.0.0”)中断;
开关(it->ai_系列)
{
案例a_unsec:

cout您的代码存在多个问题:

  • 首先,您正确地调用了
    getaddrinfo()
    ,但随后您完全丢弃了结果,而没有使用它们
  • 您调用了
    listen()
    ,但似乎打算建立传出连接;
    listen()
    用于侦听传入连接
结构中填写
sockaddr\u时,不要使用
getaddrinfo()
中的信息,而是忽略它并假定为IPv4。这部分代码应该废弃
  • 不需要显式检查返回的地址族。您不会得到任何计算机无法处理的地址族
  • 您似乎正在编写一个方法,该方法可以执行多个操作,即创建传出连接和接受传入连接。方法只能执行一项操作
  • 让我们回到开头,建立一个最小的传出连接。这里我省略了与创建连接没有直接关系的任何内容(例如,调用
    WSAAsyncSelect()
    和其他属于不同方法的内容):

    需要注意的是
    getaddrinfo()
    为您处理所有繁重的工作。它返回的结构包含创建连接所需的所有信息;您只需使用它

    如果需要连接信息,如地址和家庭,可以将其复制到
    rp
    之外,并在超出范围之前将其存储在某个位置


    编写单独的方法来处理传入连接是留给读者的一个练习。您的示例代码似乎部分基于上的示例;上有更好的示例(示例代码在Windows上的更改非常小).

    使用switch似乎还可以-如果您想获得“新奇”,可以使用函数指针数组和使用ai_族的索引来检索函数指针。
    // 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!
    }