Edmund
Edmund

Reputation: 737

select for multiple non-blocking connections

I have a single threaded program. It sends message to four destinations every five seconds. I don't want connect() to be blocked. So I am writing my program like this:

int              j, rc, non_blocking=1, sockets[4], max_fd=0;
struct sockaddr  server=get_server_addr();
fd_set           fdset;
const struct timeval  conn_timeout = { 2, 0 }; /* 2 seconds */

for (j=0; j<4; ++j)
{
    sockets[j]=socket( AF_INET, SOCK_STREAM, 0 );
    ioctl(sockets[j], FIONBIO, (char *)&non_blocking);
    connect(sockets[j], &server, sizeof (server));
}

/* prepare fd_set */
FD_ZERO ( &fdset );
for (j=0;j<4;++j)
{
    if (sockets[j] != -1 )
    {
        FD_SET ( sockets[j], &fdset );
        if ( sockets[j] > max_fd )
        {
             max_fd = sockets[j];
        }
    }
}

rc=select(max_fd + 1, NULL, &fdset, NULL, &conn_timeout );
if(rc > 0)
{
    for (j=0;j<4;++j)
    {
        if(sockets[j]!=-1 && FD_ISSET(sockets[j],&fdset))
        {
            /* send() */
        }
    }
}

/* close all valid sockets */

However, it seems select() returns immediately after ONE file descriptor is ready instead of blocking for conn_timeout (2 seconds). So in this case how can I achieve my targets?

  1. The program continues if all sockets are ready.
  2. The program can block there for 2 seconds if any one of sockets are not ready.

Upvotes: 0

Views: 173

Answers (3)

Eugen Rieck
Eugen Rieck

Reputation: 65274

There are three basic approaches:

If you want to stay strictly portable you need to iterate:

  • calculate end time from current time and timeout of your choice
  • Cycle:
  • -- Create fdset with those fds not yet ready
  • -- calculate max time to wait
  • -- select()
  • -- remeber those fds that are now ready
  • -- break if end time reached or all fds ready
  • End cycle
  • Now you have knowledge of the ready fds and the elapsed time

If you want to stay portable, but can use threads:

  • start n threads
  • select on one fd per thread
  • join all threads

If you do not need to be portable: Most OSes have a facility for such a situation, e.g. Windows/.NET has WaitAll (together with async send and an event)

Upvotes: 1

zwol
zwol

Reputation: 140540

Yeah, select was designed on the assumption that you would want to service each socket as soon as it became ready.

If I understand what you're trying to do, then the simplest way to accomplish it will be to remove each socket from the fdset as it becomes ready. If there are any sockets left in the set, use gettimeofday to adjust the timeout downward, and call select again. When the set is empty, all four sockets are usable and you can proceed.

Upvotes: 4

user207421
user207421

Reputation: 310884

I don't see the connection between your stated targets and your stated problem. You are correct in saying that select() blocks until at least one socket is ready, but according to target #2 above that is exactly what you want. There's nothing in your stated targets about blocking until all four sockets are ready at the same time.

You should also note that sockets are almost always ready for writing, unless the send buffer is full, which means the receiver's receive buffer is full, which means the receiver is slower than the sender. So using select() alone as the underlying write timer isn't a good idea.

Upvotes: 0

Related Questions