linux c socket connect nonblocking poll

Viewed 170

I am using the below code for non-blocking connect() on some IOT device with connected Internet via WiFi. Sometimes it happens that the WiFi drops and in 99% of those cases the code exits with rc != 1, meaning connect() failed.

But, for some reason, even if there is no Internet connected, sometimes I get back that poll() succeeded with the POLLOUT event and SO_ERROR says no error. Is this a bug in poll(), or am I checking it wrong? Why would I get connect() success, which is impossible?

sockfd = socket(AF_INET, SOCK_STREAM, 0);

addr = (struct sockaddr*)&address;
addrlen = sizeof(struct sockaddr_in);
int rc = -1;
// Set O_NONBLOCK
int sockfd_flags_before;
if ((sockfd_flags_before = fcntl(sockfd, F_GETFL, 0) < 0)) return -1;
if (fcntl(sockfd, F_SETFL, sockfd_flags_before | O_NONBLOCK) < 0) return -1;
// Start connecting (asynchronously)
struct timespec start;
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &start) < 0) { LOG_ERROR("Cannot get time"); return false; }
do {
    if (connect(sockfd, addr, addrlen) < 0) {
        // Did connect return an error? If so, we'll fail.
        int err = errno;
        if ((err != EWOULDBLOCK) && (err != EINPROGRESS)) {
            rc = -1;
        }
        // Otherwise, we'll wait for it to complete.
        else {
            // Wait for the connection to complete.
            do {
                // Calculate how long until the deadline
                if (clock_gettime(CLOCK_REALTIME, &now) < 0)
                {
                    rc = -1; break;
                }
                int elapsed = Util::TimespecDiffMs(&now, &start);
                int remaining = timeout_ms - elapsed;
                if (remaining <= 0) { rc = 0; break; }
                // Wait for connect to complete (or for the timeout deadline)
                struct pollfd pfds[] = { {sockfd, POLLOUT,NULL } };
                rc = poll(pfds, 1, remaining);
                // If poll 'succeeded', make sure it *really* succeeded
                if (rc > 0) {


                    int error = -1; socklen_t len = sizeof(error);
                    int retval = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
                    if (retval == 0) errno = error;
                    if (error != 0) rc = -1;
                }
            }
            // If poll was interrupted, try again.
            while (rc == -1 && errno == EINTR);
            // Did poll timeout? If so, fail.
            if (rc == 0) {

                errno = ETIMEDOUT;
                rc = -1;
            }
        }
    }
} while (0);
0 Answers
Related