Wednesday, June 26, 2013

C/C++: Async TCP Connection Failure Detection on OSX / BSD

As the hard-core C programmers know, the connect(2) function can operate in to modes:

  • blocking: the program will stop running until connection succeeds/fails/times out (latter can be as long as 30 seconds!)
  • non-blocking: connection is initiated only, and the socket's file descriptor can be checked if connection has completed or not (as a standard, check for writability with select(2)).
Whenever select(2) returns, we must be able to check if our connection attempt was the triggering event, that is, if the connection attempt has been finished or not.


One might believe the manuals and tutorials, which basically tell following:

To check the state of an asynchronous connection attempt, use getsockopt(2) to retreive the SO_ERROR value. If this is 0 (ESUCCESS), the connection has succeeded. If EINPROGRESS, the attempt has not been finished yet. Otherwise, the attempt has failed.
Problem: this is only true for Linux. On BSD (and yes, this includes OSX), when a connection is refused, getsockopt(2) will still return ESUCCESS. Even more strange, a write attempt to this socket simply makes the program exit - without a core dump.

What then?

According to this excellent summary, we are best off double-checking the state of the socket. The best option listed is issuing a getpeername(2) call, which either succeeds (connection established!) or fails (connection failed).

In code:
  // assuming connection is already in progress, select(2) has returned,
  // socket file descriptor is fd_:

  int err = -1;
  int len = sizeof(err);
  if (getsockopt(fd_, SOL_SOCKET, SO_ERROR, (int*)&err,
                 (socklen_t*)&len) != 0) {
    handleConnectionError("getsockopt error");
    return;
  }   
    
  struct sockaddr sa;
  socklen_t saLen = sizeof(sa);
  int gpnErr = getpeername(fd_, &sa, &saLen);
  
  if (err == 0 && gpnErr == 0) {
    handleConnected();
    return;
  }

  if (err == EINPROGRESS) {
    // do one more select(2) run on the file descriptor fd_
  }





No comments:

Post a Comment