Non blocking connect to loopback address (127.0.0.1 or localhost)
When I do a non blocking connect to the loopback address from the iOS Simulator (which probably uses the same TCP stack as the Mac and FreeBSD), I see that connect always succeeds, even when the server process isn't running.
I detect t开发者_JS百科hat connect succeeded by using select() with zero timeout. So, as long as select() returns 0, I assume that the connection is in progress, if it returns -1 I fail with an error and if it returns 1, the socket is ready for reading (as the server must have responded), and I begin reading after reporting that the connect succeeded.
This works well for all addresses except the loopback. On the loopback select() always returns 1, even when there is no server running. So, I begin reading, which fails and I handle it. But I should have detected this through select()!
You are getting the error before that, right on connect()
.
Before going to proceed with select()
, check that errno
is EINPROGRESS
and not something else. In *BSD, connections to a non-listening port on a localhost
error out (or may error out) immediately.
I just ran a very simple test like this (headers skipped):
int
main(void)
{
int fd;
int r;
struct sockaddr_in remote;
struct hostent *he;
he = gethostbyname("localhost");
if (he == NULL)
return -1;
memcpy(&remote.sin_addr, he->h_addr, sizeof(remote.sin_addr));
remote.sin_port = htons(9671);
remote.sin_family = AF_INET;
fd = socket(PF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
r = connect(fd, (struct sockaddr *)&remote, sizeof remote);
if (r < 0) {
perror("connect");
}
return 0;
}
With nothing listening on port 9671
, I got:
- on Linux:
connect: Operation now in progress
- on FreeBSD:
connect: Connection refused
Of course, it is always a good idea to check the error codes of all syscalls (something the example above does not do for simplicity's sake - it's just an illustration, after all).
The problem was that I was relying on select() to tell me if the connect was successful. Select only tells you if something changed on that fd. I should've actually called connect() again on the socket and verify that the if it fails, errno is either EINPROGRESS, ECONN or EALREADY. Except for ECONN, all the other values mean that we should retry; ECONN means it's already connected. Any other errno value means we failed to connect.
精彩评论