Search code examples
socketsbsd

BSD socket connect + select (client)


There must be something wrong in the below code but I don't seem to be able to use a client connect, non blocking in combination with a select statement. Please ignore the below lack of error handling.

I seem to have two issues 1. select blocks until timeout (60) if I try to connect port 80 on an internet server 2. trying to connect a existing or non existing port on 127.0.0.1 always instantly returns the select with no way to distinction between success or failure to connect.

What am I missing in my understanding of BSD nonblocking in combination with select?

fd_set readfds;
FD_ZERO(&readfds);

struct timeval tv;
tv.tv_sec = 60;
tv.tv_usec = 0;

struct sockaddr_in dest;
int socketFD = socket(AF_INET, SOCK_STREAM, 0);

memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(9483);

long arg;
arg = fcntl(socketFD, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(socketFD, F_SETFL, arg);

if (connect(socketFD, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0 && errno == EINPROGRESS) {

    //now add it to the read set
    FD_SET(socketFD, &readfds);
    int res = select(socketFD+1, &readfds, NULL, NULL, &tv);

    int error = errno;
    if (res>0 && FD_ISSET(socketFD, &readfds)) {
        NSLog(@"errno: %d", error); //Always 36
    }
}

Solution

  • errno is set in your original attempt to connect -- legitimately: that is, it's in-progress. You then call select. Since select didn't fail, errno is not being reset. System calls only set errno on failure; they do not clear it on success.

    The connect may have completed successfully. You aren't checking that though. You should add a call to getsockopt with SO_ERROR to determine whether it worked. This will return the error state on the socket.

    One other important note. According to the manual page (https://www.freebsd.org/cgi/man.cgi?query=connect&sektion=2), you should be using the writefds to await completion of the connect. I don't know whether the readfds will correctly report the status.

    [EINPROGRESS]   The socket is non-blocking and the connection cannot
                    be completed immediately.  It is possible to select(2)
                    for completion by selecting the socket for writing.
    

    See also this very similar question. Using select() for non-blocking sockets to connect always returns 1