0

Here is the code I ended up doing for an asynchronous client:

struct addrinfo *currentAddress = NULL;

void GetAddresses(std::string hostname, int port)
{
    struct addrinfo hints;

    std::cout << "Get adresses for hostname " << hostname << " port " << port << std::endl;

    std::memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_family = AF_UNSPEC;        /* Allow IPV4 or IPV6 */
    hints.ai_socktype = SOCK_STREAM;    /* Datagram socket */
    hints.ai_flags = 0;
    hints.ai_protocol = 0;              /* Any protocol */

    std::string portStr;
    portStr = std::to_string(port);

    int status = getaddrinfo(hostname.c_str(), portStr.c_str(), &hints, &currentAddress);

    if (status != 0)
    {
        std::stringstream ss;
        ss << "Cannot resolve hostname " << hostname << ". Error: " << gai_strerror(status);
        throw std::runtime_error(ss.str());
    }
}

void StepNextAddress()
{
    currentAddress = currentAddress->ai_next;
}

void Connect(const std::string& address, int port)
{
    /*
     * Get all addresses for connection
     */
    GetAddresses(address, port);

    bool success = false;

    while (currentAddress != NULL)
    {
        int s = socket(currentAddress->ai_family, currentAddress->ai_socktype, currentAddress->ai_protocol);

        if (s == -1)
        {
            freeaddrinfo(currentAddress);
            std::stringstream ss;
            ss << "Error creating socket. Out of resources.";
            throw std::runtime_error(ss.str());
        }

        std::cout << "Socket created: " << s << std::endl;

#ifdef _WIN32
        unsigned long on = 1;
        ioctlsocket(s, FIONBIO, &on); // Make it non blocking
#else
        fcntl(s, F_SETFL, O_NONBLOCK);  Make if non blocking
#endif

        /*
         * Connect to socket
         */
        int status = connect(s, currentAddress->ai_addr, currentAddress->ai_addrlen);

        if (status < 0 && errno != EINPROGRESS)
        {
            freeaddrinfo(currentAddress);
            std::stringstream ss;
            ss << "Error connecting socket to " << address << " port " << port << ".";
            throw std::runtime_error(ss.str());
        }

        /* 
         * Wait socket to get ready to select
         */
        fd_set readSet, writeSet, exceptSet;
        FD_ZERO(&readSet);
        FD_ZERO(&writeSet);
        FD_ZERO(&exceptSet);
        timeval tout;
        tout.tv_sec = CONNECT_TIMEOUT_SECONDS;

        int retval = select (s + 1, &readSet, &writeSet, &exceptSet, &tout);

        if (retval == -1)
        {
            freeaddrinfo(currentAddress);
            std::stringstream ss;
            ss << "Error selecting socket.";
            throw std::runtime_error(ss.str());
        }

        /*
         * Timeout. Try next resources
         */
        if (retval == 0)
        {
            std::cout << "Connection timedout. Trying next resource." << std::endl;
#ifdef _WIN32
            _close(s);
#else
            close(s);
#endif
            StepNextAddress();
            continue;
        }


        /*
         * Wait socket to be ready to work
         */
        int result = 0;
        socklen_t result_len = sizeof(result);

        int sts = getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &result, &result_len);

        if (sts < 0)
        {
            freeaddrinfo(currentAddress);
            std::stringstream ss;
            ss << "Error getting socket option.";
            throw std::runtime_error(ss.str());
        }

        if (result != 0)
        {
            freeaddrinfo(currentAddress);
            std::stringstream ss;
            ss << "Error getting socket resources.";
            throw std::runtime_error(ss.str());
        }
    }

    freeaddrinfo(currentAddress);

    std::cout << "CONNECTED!!!!!!!!!!!!" << std::endl;
}

int main()
{
    Connect("test.test.com", 21);
}

I don´t understand yet the selectusage together with the getsockopt. Are they necessary or in my case select would be enough ?

In that case, the select is thowing an error... What is wrong with the select code ?

Also, what about the error treatments here... The only case I´m trying to connect the next address in list is on timeout... Does it make sense ?

To finish, comments in the code are well appreciated.

Mendes
  • 17,489
  • 35
  • 150
  • 263
  • You do not need non-blocking sockets with select. – SergeyA Apr 15 '16 at 16:57
  • I did not get the idea... I want to set timeout for a connection. If the socket does not connect in a certain time (10sec), then abort the connection attempt.... I thought the way to go was non-blocking with select... If I remove `select` how can I timeout the connection attempt ? `getsockopt` does not offer me a timeout parameter... I´m sure I´m messing things here... help appreciated. – Mendes Apr 15 '16 at 17:00
  • No, you use blocking with select. Once select tells you the socket is ready, you use the socket, and you are guaranteed not to block on it **this time**. Do not remove select, leave it alone. Do not make your socket non-blocking. – SergeyA Apr 15 '16 at 17:02
  • Please elaborate.... [link](http://stackoverflow.com/questions/11286094/using-select-for-non-blocking-sockets-to-connect-always-returns-1) [link](http://forums.codeguru.com/showthread.php?511816-How-to-use-select-in-non-blocking-socket) and several others on google using select with non-blocking sockets... You mean I should remove `ioctlsocket(s, FIONBIO, &on);`and the later `getsockopt`? If I remove `ioctlsocket(s, FIONBIO, &on);` the socket will stuck on connect if there is no server available ?!?!? – Mendes Apr 15 '16 at 17:06
  • I didn't get that you are using non-blocking sockets for `connect`. Yes, for polling `connect` you need a non-blocking socket. – SergeyA Apr 15 '16 at 17:21

0 Answers0