Question

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?

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());
            }
        }
    }
Was it helpful?

Solution

Your code has multiple issues:

  • First, you correctly called getaddrinfo(), but then you completely threw away the results without using them.
  • You called listen() but you appear to intend to make an outgoing connection; listen() is meant to listen for incoming connections.
  • Instead of using the information from getaddrinfo() you ignore it and assume IPv4 when filling out your sockaddr_in structure. 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, i.e. 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 (e.g. the call to WSAAsyncSelect() and other stuff which belongs in separate methods anyway):

// 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. 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.

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; the Linux getaddrinfo manual page has much better examples (the sample code actually works with minimal change on Windows).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top