UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

#1 2002-07-27 12:15 AM

HectorLasso
Administrator
From: Colombia
Registered: 2002-06-12
Posts: 352

Re: 6.4 - How to use select routine

6
From: Starch Melo de Souza

I need to use select routine for receive several response at the same time. How to write the code??? Thanks.

From: Michael Song

int sock_ready(sock) 
int sock; 
{ 
    int             res; 
    fd_set          sready; 
    struct timeval  nowait; 

    FD_ZERO(&sready); 
    FD_SET((unsigned int)sock,&sready); 
    /*bzero((char *)&nowait,sizeof(nowait));*/ 
    memset((char *)&nowait,0,sizeof(nowait)); 

    res = select(sock+1,&sready,NULL,NULL,&nowait); 
    if( FD_ISSET(sock,&sready) ) 
        res = 1; 
    else 
        res = 0; 

    return(res); 
}

From: S.Murali Krishna

Explanation of above select code.
Q: How to use select call for multiplexing.

Ans: First you are declaring a fd_set variables.
so that this can be used in select call.
then depending on the (read/write) on socket you are using
Add the socket to the corresponding fd_sets using FD_SET()
call. Before that you have to clear any garbage value there
using FD_ZERO(). Then Select call requires timeout ie. maximum
time to wait for I/O arrival on fds. We are going to wait indefinetly
so we make the timeval structure ZERO by making any calls
specified in the comments. Call the select call with
First Argument as 1 greater than the greater numbered file
descriptor in the second , third or fourth argument.
since we are going to read the socket place it in the
second argument fd_set (as already we done). and give the
timeval structure as Last argument to select so select will
wait indefinetly on the socket and if any I/O comes
it will return with success value.
Then We have to check for the Existense of the socket descriptor
in the readfds fd_set using FD_ISSET() macro. if it returns
true then we can continue any further reading with that socket.
Thanks..

From: Dennis Fleurbaaij

mmm there are some things in the code that are
quite err.. unforunate.

First res is set to the output of the select() call
and after that it's overwritten. Youb better do a
bit of errorchecking and if not, you can
leave out res all together and return(n)

From: legend hacker

Um, res is an int, not a pointer, it wont be overwritten, cos its copied
to the calling function by value, and theres no n. Why else is the
code unfortunate?

From: what

are you drunk? res isn't passed to anything, who cares if its a pointer or a int, and why bother saving the return value anyway if you never read it and turn around and clobber the value in an if-else? dennis is making a simple point and you <b>suck</b>.

From: Loco

The code is really worthless for the purpose of the question:
"I need to use select routine for receive several response at the same time. How to write the code??? Thanks."

As I see it, this guy needed someone to explain him how to use select() to be able to receive from different connections at the same time. The code written by Michael only select()s on one socket. It doesn't do any error checking and you cannot rely on the contents of the sets after an error, so the results can be wrong.

I don't know what "legend hacker" means by pointer because it isn't mentioned in the previous answers.

The code is just an example of how to use select() but with some errors in it and not very functional, it has zero timeout which makes select() return inmediately. You can as well use non-blocking sockets with recv() to get the same functionality (plus more).

However, no one has given example code to use select() with multiple sockets, so here is my code:

/* 
   This piece of code waits for new connections on a socket, and reads messages received from 
   other sockets. It adds each new connection to the set so it can read data from new connections 
   it also deletes the connections that are closed by the other side, and frees resources 
   associated with them. 
   It doesn't do any timeout, nor does it use the write and exception sets. 
   It just sits forever waiting for data to be available for recv() 

   srvsock -> Is a socket that is listening for new connections 
*/ 
fd_set readset, tempset; 
int newsoc, len, i, max, res; 
struct sockaddr_in addr; 
char buffer[1025]; 

// First: Set the "sets" 
FD_ZERO(&readset); 
FD_SET(srvsock, &readset); 
max = srvsock; 

do { 
   // Copy readset into the temporary set 
   memcpy(&tempset, &readset, sizeof(readset)); 
   // Wait indefinitely for read events 
   res = select(max+1, &tempset, NULL, NULL, NULL); 
   if (res < 0) { 
      // Error processing here, i just abort!!! 
      break; 
   } 
   // This should never happen because we are not using timeout!!! 
   if (res == 0) { 
      printf("What the hell!!!\n"); 
      continue; 
   } 

   // Process new connections first 
   if (FD_ISSET(srvsock, &tempset)) { 
      // New connection, do something (SCREAM?) 
      len = sizeof(addr); 
      newsoc = accept(srvsock, &addr, &len); 
      // I should check the result here... 
      FD_SET(newsoc, &readset); 
      if (newsoc > max) 
         max = newsoc; 
      FD_CLR(srvsock, &tempset); // Why? Well, i don't want to read from the server!!! 
   } 

   // Process events of other sockets... 
   for (i=0; i<=max; i++) { 
      if (FD_ISSET(i, &tempset)) { 
         // Process data from socket i 
         res = recv(i, buffer, 1024, 0); 
         if (res <= 0) { 
            // Closed connection or error 
            FD_CLR(i, &readset); 
            // You should check max here so i leave it up to you 
            // ... 
            // Do some cleaning 
            shutdown(i, 2); 
            close(i); 
         } 
         else { 
            // Process data in buffer 
            printf("%d bytes read: [%.*s]\n", res, res, buffer); 
         } 
      } 
   } 
   // Done processing 
} while (1); 

I hope this helps... 
:) (HAL)


From: Mark

Loco, don't you need to FD_SET all the sockets 0..max?
Otherwise your select() is only waiting for events on
Srvsock and not the other sockets.

From: Rob Seace

He is doing the FD_SET() of the other sockets... As
soon as he accept()'s a new connection, he FD_SET()'s
it into "readset"... (Which gets copied into the
"tempset" used in the select() call...) Looks perfect
to me...

From: fcrick

I'm not really too knowledgable here but i think...

I think the issue you are pointing out is that he checks every socket from 0..max, which is slightly extrainious if socket number get high (do they?), instead of just keeping track of the individual socket numbers as connections are made...

From: eglo
Added on: 2002-02-21 18:26:26

Loco, I'm kinda new to socket development. I apreciate the example you created however would you be willing to share samples for turning around the transaction using select() to write some data back via the same FD after the data received has been processed.

I guess I should preface my above request with the question: Can it be done ?

If it can be done, can it be perfomed independantly in such a way as to not impact the code that's multiplexing the read sockets. I hope I'm making sense here.


Thanks

From: Loco
Added on: 2002-02-21 19:24:14

I really don't understand your answer.

If you just need to reply to the received message then take a look at this modification (it turns the previous code into something similar to an echo server...):

// Process events of other sockets... 
   for (i=0; i<=max; i++) { 
      if (FD_ISSET(i, &tempset)) { 
         // Process data from socket i 
         res = recv(i, buffer, 1024, 0); 
         if (res <= 0) { 
            // Closed connection or error 
            FD_CLR(i, &readset); 
            // You should check max here so i leave it up to you 
            // ... 
            // Do some cleaning 
            shutdown(i, 2); 
            close(i); 
         } 
         else { 
            // Process data in buffer (echo back) 
            send(i, buffer, res, 0); 
            printf("%d bytes recv'd: [%.*s]\n", res, res, buffer); 
         } 
      } 
   } 
   // Done processing


If it is not what you were asking about, then it'd be great if you are more specific (maybe an example of what you want to do)

Regards

From: Loco
Added on: 2002-02-21 19:53:46

To fcrick:
You've got a point. I will try to explain it:

While Mark's original question is clear, he didn't catch the code that added every new socket to the read set, and he thought it would make select() only work for the server socket. Rob's comment is accurate (especially on the "Looks perfect to me...", it's a remarkable expression coming from him, I feel really honored! :)

But your question is good.
While the code doesn't keep track of every single file descriptor, it just increases a variable to the currently max file descriptor.
The read set is the one that is tracking the file descriptors, just by setting/clearing it when connected/disconnected. As not always the max file descriptor will be the one closed, the read set will be something like this if several connections are closed (it's just one of the many, many posibilities):
1010000000000000000000000010000000000000000000000000001
(1 means set, 0 means not set)

So imagine the server received 1000 connections, and 998 connections were closed, except the first and the last (meaning the greatest value). I think I read somewhere that as the first parameter grows bigger, the select() statement will take longer to process... (I don't remember where). So in this case the code would add additional overhead to the select() processing.

I'll try to correct it with the following modification:

// Process events of other sockets... 
   i = 0; 
   while (i<=max) { 
      if (FD_ISSET(i, &tempset)) { 
         // Process data from socket i 
         res = recv(i, buffer, 1024, 0); 
         if (res <= 0) { 
            // Closed connection or error 
            // I assume an error must close the connection - this is not always true! 
            if (i != max) { 
               // Not the last file descriptor 
               dup2(i, max); 
               close(max); 
               if (FD_ISET(max, &tempset)) 
                  FD_CLR(max, &tempset); 
               else 
                  FD_CLR(i, &tempset); 
               max--; 
            } 
            else { 
               // The last file descriptor 
               FD_CLR(i, &readset); 
               close(i); 
               max--; 
               i++; 
            } 
         } 
         else { 
            // Process data in buffer 
            printf("%d bytes read: [%.*s]\n", res, res, buffer); 
            i++; 
         } 
      } 
   } 
   // Done processing


Great, now I turned my beatiful code into this mess. But, I like to code, post and then think "will-this-work???"

The idea is:
If one socket is closed, then don't leave empty bits in the set (that is those 0 I showed before).
If the file descriptor is not equal to the max value, then dup2(max, curren_file_descriptor) which, by the way, closes the current_file_descriptor for us. Decrease max by one, and close max. (One ugly thing I had to do is check whether max also had any event, and set or clear the tempset for current_file_descriptor in order to process its events).

I changed the "for()" loop for a "while()" loop (it sounds weird, don't you think? ;) because I didn't like to modify max and i inside the for loop ... (it looked ugly), so I may have forgotten some increment or something.

I guess this works (haven't tested it, I never do). And, I'm not really sure if this code would really help speed a normal application at all...

Any comments are welcome.

Regards,

Loco
:D (HAL)

From: Rob Seace
Added on: 2002-02-22 09:36:26

I think you reversed your args to the dup2() call... I
assume you're trying to move "max" down to "i"'s place,
rather than vice versa, right?

But, the code still isn't quite right, I don't think...
It assumes every socket FD is only 1 apart... Which won't
necessarily always be true, if you happen to open up any
other files at any point, or do anything else to use up an
FD... So, your "max--" will leave "max" set to some
possibly non-socket FD value, which may not be one you
actually have in your fd_set... So, you DON'T want to go
dup2()'ing it, and changing its value to a lower FD,
because something else may be relying on using it as it
already is...

I don't think the performance penalty incurred by the for()
loop through all of the FDs is anything much to worry too
much about, even in a sparsely populated fd_set, with a
really high "max" setting... I'm sure there's certainly
SOME penalty (and, I know I've seen benchmarks showing
select()'s performance to degrade heavily when you crank up
the value of its first arg), but I question whether it'd
really be noticable for the vast majority of apps... If
you're writing something really time-critical, MAYBE it's
something to worry about, though...

You could also contemplate digging into the guts of fd_set
to get at the bits, and using fast bit-finding functions
like ffs(), too... Also pretty ugly, but possibly worth
it in some rare cases... (You'd still be left with the
select() overhead, unless you brought down the "max" value,
though...)

From: Loco
Added on: 2002-02-22 11:20:13

Rob,

You're right. I swapped the i and max parameters in dup2().

I am assuming that the program DOES NOT open any other file...

Offline

Board footer

Powered by FluxBB