Search code examples
c++socketstcpnetwork-programmingblocking

How to get out from a TCP blocking connect() call?


    int tcp_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    

    struct sockaddr_in remoteaddr;
    struct sockaddr_in localAddr;
    short local_port = 22222;
    short remote_port = 33333;

    // local addr
    localAddr.sin_family      = AF_INET;
    localAddr.sin_port        = htons(local_port);
    localAddr.sin_addr.s_addr = 0xC0A80AA5; // we don't give a shit
    int addrLen = sizeof(struct sockaddr_in);

    //Now bind TCP to local addr
    int result = bind(tcp_sock,(struct sockaddr*)&localAddr,addrLen);
    if (result < 0)
    {
        perror("\nbind failed");
        close(tcp_sock);
        return -1;
    }

    result = connect(tcp_sock, (struct sockaddr*)&remoteaddr, sizeof(struct sockaddr_in));
    printf("\nConnect returned %d, error no: %d", result, errno);

Here the connect() call fails after a long time. Is there any way that I can make connect function return after a time of my choice? I tried calling close() from another thread but this doesn't change anything.


Solution

  • Put the socket into non-blocking mode before calling connect(), then you can use select() to specify a timeout. select() will tell you whether the connection succeeds or times out. If successful, you can put the socket back into blocking mode, if you want. If failed/timeout, close the socket instead.

    int tcp_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    
    
    ...
    
    int flags = fcntl(tcp_sock, F_GETFL, 0);
    fcntl(tcp_sock, F_SETFL, flags | O_NONBLOCK);
    
    int errCode = 0;
    
    result = connect(tcp_sock, ...);
    if (result < 0)
    {
        errCode = errno;
    
        if (errCode == EINPROGRESS)
        {
            fd_set wfd;
            FD_ZERO(&wfd);
            FD_SET(tcp_sock, &wfd);
    
            struct timeval timeout;
            timeout.tv_sec = ...;
            timeout.tv_usec = ...;
    
            result = select(tcp_sock+1, NULL, &wfd, NULL, &timeout);
            if (result > 0)
            {
                socklen_t len = sizeof(errCode);
                result = getsockopt(tcp_sock, SOL_SOCKET, SO_ERROR, &errCode, &len);
                if (result < 0)
                    errCode = errno;
                else
                    result = (errCode == 0) ? 0 : -1;
            }
            else if (result == 0)
            {
                errCode = ETIMEDOUT;
                result = -1;
            }
            else
            {
                errCode = errno;
            }
        }
    }
    
    if (result == 0)
    {
        // connected
        fcntl(tcp_sock, F_SETFL, flags);
        ...
    }
    else
    {
        // error, use errCode as needed
        ...
    }