|
#1
|
|||
|
|||
|
6.4 - How to use select routine
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 Code:
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);
}
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: 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)
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...): Code:
// 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
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): 10100000000000000000000000100000000000000000000000 00001 (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: Code:
// 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
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... Last edited by i3839; 08-30-2006 at 01:05 PM. Reason: Removed & and other forum convertion garbage. |
![]() |
| Thread Tools | |
| Display Modes | |
|
|