6

I'm trying to set a timeout on an accept socket function without success. I would like the accept function blocks until the end of the timeout delay. Is it possible without setting the accept function non-blocking ? I have tried out many possibilities without success.

Thanks for your answers.

Below is my code :

struct timeval tv;
fd_set readfds;

tv.tv_sec = 1;
tv.tv_usec = 0;

int s, s_remote;
struct sockaddr_un remote;
struct sockaddr_un local;

if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
  perror("socket");
  exit(1);
}

FD_ZERO(&readfds);
FD_SET(s, &readfds);

if (select(s+1, &readfds, NULL, NULL, &tv) > 0) {

  printf("Waiting for a connection...\n");

  memset(&local, 0, sizeof(local));
  local.sun_family = AF_UNIX;
  strcpy(local.sun_path, SOCK_PATH);
  unlink(local.sun_path);

  if (bind(s, (struct sockaddr *)&local, sizeof(local)) == -1) {
    perror("UnixSocketClient :: error bind.\n");
    close(s);
    return -1;
  }

  if (listen(s, 5) == -1) {
    perror("UnixSocketClient :: error listen.\n");
    close(s);
    return -1;
  }

  socklen_t remote_len = sizeof(remote);
  printf("Accept :\n\r");

  if ((s_remote = accept(s, (struct sockaddr *)&remote, &remote_len)) == -1) {
    perror("UnixSocket :: error accept.\n");
    return -1;
  }
  printf("Client accepted\n\r");
}
Tekros
  • 71
  • 1
  • 1
  • 4
  • Possible duplicate of [How to accept socket with timeout](http://stackoverflow.com/questions/14045064/how-to-accept-socket-with-timeout) – Andrew Henle Feb 04 '16 at 16:52
  • Since you already have a `select` loop design, why not call `bind` and `listen` before entering the `select` loop, set the socket non-blocking, and use `select` to know when to call `accept`? (Your design is already broken being halfway between a blocking and non-blocking design. Why are you `select`ing on `s`?) – David Schwartz Feb 04 '16 at 18:23

2 Answers2

7

Your code makes no sense. You have to call:

  • socket()
  • bind()
  • listen()
  • select()
  • accept()

in that order.

user207421
  • 305,947
  • 44
  • 307
  • 483
2

On the listening socket call select() with a time-out set and only call accept() if select() did not time out.

Update:

The above approach (which in fact is the same as in this answer) indeed introduces a race, as discussed in the comments to this answer. If select return indicating an incoming connecting, there is a (short) delay until accept is called. If during this delay the connection is gone, then accept() will block.

The only way to manoeuvre around this is to use a non-blocking socket when calling accept().


If the requirement (for what ever reasons) is to use a blocking socket, then a possible solution would be to send a signal to the process blocked in accept(), which made it return with a value of -1 and having errno set EINTR.

void handle_sigalarm(int sig)
{
  /* do nothing */
}


  ...

  sigaction(SIGALRM , &(struct sigaction){handle_sigalarm}); /* Setup handler for 
     alarm signal to override the default behaviour which would be to 
     end the process . */

  alarm(3); /* Arranges for a SIGALRM signal to be delivered to the calling
   process in 3 seconds. */

  {
    int accepted_socket = accept(...);
    int errno_save = errno;
    alarm(0); /* Cancel any pending alarm. */
    if (-1 == accepted_socket)
    {
      if (EINTR == errno_save)
      {
        fprintf(stderr, "accept() timed out\n");
      }

      /* handling other errors here. */
    }

    ...
alk
  • 69,737
  • 10
  • 105
  • 255
  • He said he didn't want to set the socket non-blocking. With your scheme, he could wait forever if the connection is gone by the time he gets to `accept`. – David Schwartz Feb 04 '16 at 18:22
  • @DavidSchwartz I don't understand your point. How can he wait forever if he selects with a timeout? – user207421 Feb 04 '16 at 18:31
  • 2
    @EJP Say there's an incoming connection, he returns from `select`, but by the time he calls `accept`, the connection is gone. His `accept` can wait forever (or until a new connection comes in). So to use this method, he *must* set the socket non-blocking, which is what he specifically said he didn't want to do. The `select` function is just a report of current status (just like `access` or `stat`) -- it doesn't come with future guarantees. (This is a common mistake and has caused serious defects with significant security implications in the past.) – David Schwartz Feb 04 '16 at 20:24
  • @David Schwartz that looks like a design flaw in Unix. On windows, an accept is guaranteed not to block after a select (see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85).aspx) – symbiont Apr 14 '18 at 09:12
  • 2
    @symbiont You misunderstand what it says. it says "and a connection is pending". There is no guarantee it remains pending, and that would be strange. Select is a status-reporting function, having it cause a pending connection to remain pending is bizarre. It would be an odd design flaw in Windows if it somehow cause a select call to require a connection to remain pending forever. – David Schwartz Apr 18 '18 at 12:00
  • @DavidSchwartz interesting. i was referring to the text "If the socket is currently in the listen state, it will be marked as readable if an incoming connection request has been received such that an accept is guaranteed to complete without blocking". i was not saying that the connection would remain pending (i honestly don't know). i was only concerned with the blocking behavior. why would it be bizarre to keep a connection pending until it is accepted? (i don't understand and i'm interested) – symbiont Apr 30 '18 at 08:23
  • 2
    @symbiont It would be bizarre because that would mean that even if the connection closes or errors, it still must remain pending indefinitely. It's bizarre for an implementation to have to keep track of the fact that a status-reporting function was called. It's as bizarre as a requirement that a report that a file system had free space guaranteed that a future write would succeed. Note that the guarantee does *not* refer to an operation arbitrarily in the future. – David Schwartz Apr 30 '18 at 17:21
  • @DavidSchwartz i'm not convinced. i've tested it. after the server has called select() and the client process attempts to connect() and immediately exits before the server calls accept(), then the server does not block. the accept() call succeeds and subsequent calls like recv() simply result in an error. i understand that it's strange for a status reporting function to change something, but from it may be the only way that they can meet their garantee about not blocking. i can't say how Linux works but i don't think that connection pending and not blocking are the same thing – symbiont May 06 '18 at 14:29
  • 1
    @symbiont Your test only shows the behavior that happened to occur on your system. I've seen many times when people make assumptions based on what happened on their system and then new versions of the OS come out or other people have intermediate Winsock drivers or firewall drivers and the behavior is different. It's madness to rely on a status reporting function to provide a future guarantee. As I said, "This is a common mistake and has caused serious defects with significant security implications in the past.". You can learn from past mistakes or not -- your choice. – David Schwartz May 06 '18 at 19:23