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);