Thread: select system call usage doubt

  1. #1
    Registered User
    Join Date
    Mar 2008
    Location
    India
    Posts
    147

    select system call usage doubt

    i have a tcp concurrent server program as below.

    my doubt is in the for loop , I have written my doubt near the for loop with in comments

    If any body can share there knowledge it would give me complete understanding of the select call usage.

    Code:
    #include <stdio.h>
    
    #include <sys/types.h>
    
    #include <sys/socket.h>
    
    #include <netinet/in.h>
    
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    
    #include <string.h>
    
     
    
    #define SRV_TCP_PORT 8000
    
    #define MAX_MSG 100
    
    
    
    void errExit(char *str)
    {
         puts(str);
         exit(0);
    }
    
    int main()
    {
         int srvSockFd,newSockFd,fd;
         struct sockaddr_in srvAdr, cliAdr;
         int cliLen,n;
         fd_set readfds, testfds;
         int stat;
         char mesg[MAX_MSG];
    
         if((srvSockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    	  errExit("Can't open datagram socket\n");
    
         memset(&srvAdr, 0, sizeof(srvAdr));
         srvAdr.sin_family = AF_INET;
         srvAdr.sin_addr.s_addr = htonl(INADDR_ANY);
         srvAdr.sin_port = htons(SRV_TCP_PORT);
    
         if(bind(srvSockFd,(struct sockaddr*)&srvAdr, sizeof(srvAdr)) < 0)
    	  errExit("Can't bind local address \n");
    
         listen(srvSockFd,5);
    
         FD_ZERO(&readfds);
         FD_SET(srvSockFd,&readfds);
    
         printf("Server waiting for new connection :\n");
    
         while(1)
         {
    	  testfds = readfds;
    
    	  stat = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
    	  
    	  if(stat < 1)
    	       errExit("select error \n");
    
    	  for(fd = 0; fd < FD_SETSIZE; fd++)
    	  {
    
    
    	       if(FD_ISSET(fd,&testfds))
    	       {
    
    
    /* here once any fd is ready it will come in to loop .
    why do we need to compare is it srvSockFd or not ? */
    		    if(fd == srvSockFd)
    		    {
    			 newSockFd = accept(fd, (struct sockaddr *)0, NULL);
    			 FD_SET(newSockFd, &readfds);
    /* why cant we directly use the testfd set it selft to set the newSockFD 
    why do we need the second fd set readfd here ?
    here is readfds and testfds are reference based , i mean any change made to 
    readfds will take effect on testfds? */
    
    
    			 printf("Adding client fd: %d to readFdSet \n",newSockFd);			 
    		    }
    		    else
    		    {
    
    /* This is my fundamental doubt like when will it come to else part
    once fd is ready it will go the above block so when and how it comes to this 
    block */
    			 n = recv(fd, mesg, MAX_MSG, 0);
    			 if(n < 0)
    			      errExit("recv error \n");
    			 else if(n == 0)
    			 {
    			      close(fd);
    			      FD_CLR(fd, &readfds);
    			      printf("Removing client fd: %d from readFdSet \n",fd);
    			 }
    			 else
    			 {
    			      if(send(fd,mesg,n,0) != n)
    				   errExit("send error \n");
    			 }
    		    }
    	       }
    	  }
         }
    }

  2. #2
    Ultraviolence Connoisseur
    Join Date
    Mar 2004
    Posts
    555
    Code:
    ....
    ....
    	  for(fd = 0; fd < FD_SETSIZE; fd++)
    	  {
    
    
    	       if(FD_ISSET(fd,&testfds))
    	       {
    
    
    /* here once any fd is ready it will come in to loop .
    why do we need to compare is it srvSockFd or not ? */
    		    if(fd == srvSockFd)
    		    {
    			 newSockFd = accept(fd, (struct sockaddr *)0, NULL);
    			 FD_SET(newSockFd, &readfds);
    /* why cant we directly use the testfd set it selft to set the newSockFD 
    why do we need the second fd set readfd here ?
    here is readfds and testfds are reference based , i mean any change made to 
    readfds will take effect on testfds? */
    
    
    			 printf("Adding client fd: %d to readFdSet \n",newSockFd);			 
    		    }
    What is happening is here is that they are testing the current file descriptor that is ready to read from and checking to see if it is the "main" server socket; the socket that is handling NEW connections. If it is the "server" socket then it opens/accepts the new connection coming in and adds this NEW file descriptor to the SAME test set "readfds" you see earlier that testfds has been set to readfds.

    So if its adding new connections to the same read set as the "server" socket..now you see why it has to test the descriptor to see if its the server or a normal client connection...another term i could call the "server" socket would be the listening socket, in this case and probably a better name for it..

    Code:
    		    else
    		    {
    /* This is my fundamental doubt like when will it come to else part
    once fd is ready it will go the above block so when and how it comes to this 
    block */
    			 n = recv(fd, mesg, MAX_MSG, 0);
    			 if(n < 0)
    			      errExit("recv error \n");
    			 else if(n == 0)
    			 {
    			      close(fd);
    			      FD_CLR(fd, &readfds);
    			      printf("Removing client fd: %d from readFdSet \n",fd);
    			 }
    			 else
    			 {
    			      if(send(fd,mesg,n,0) != n)
    				   errExit("send error \n");
    			 }
    		    }
    	       }
    	  }
    ....
    ....
    The else block here is happening when the current socket "fd" is NOT the "server" socket that I talked about above. Meaning its one of the clients that we add to the read set in the FIRST block I explained above...

    So that is why it tries to read data from this socket, because it is a client...whats odd is that it just sends the data back again...kind of pointless no?

    make sense?

    EDIT: BTW please check out "man select" and "man select_tut" on any *nix/POSIX system
    you need to re-initialize your fd_sets BEFORE EACH select() call you do...
    particularly these rules:
    Select Law
    Many people who try to use select() come across behavior that is difficult to understand and
    produces nonportable or borderline results. For instance, the above program is carefully
    written not to block at any point, even though it does not set its file descriptors to non-
    blocking mode. It is easy to introduce subtle errors that will remove the advantage of using
    select(), so here is a list of essentials to watch for when using select().

    1. You should always try to use select() without a timeout. Your program should have noth-
    ing to do if there is no data available. Code that depends on timeouts is not usually
    portable and is difficult to debug.

    2. The value nfds must be properly calculated for efficiency as explained above.

    3. No file descriptor must be added to any set if you do not intend to check its result
    after the select() call, and respond appropriately. See next rule.

    4. After select() returns, all file descriptors in all sets should be checked to see if they
    are ready.

    5. The functions read(2), recv(2), write(2), and send(2) do not necessarily read/write the
    full amount of data that you have requested. If they do read/write the full amount, it's
    because you have a low traffic load and a fast stream. This is not always going to be
    the case. You should cope with the case of your functions only managing to send or
    receive a single byte.

    6. Never read/write only in single bytes at a time unless you are really sure that you have
    a small amount of data to process. It is extremely inefficient not to read/write as much
    data as you can buffer each time. The buffers in the example below are 1024 bytes
    although they could easily be made larger.

    7. The functions read(2), recv(2), write(2), and send(2) as well as the select() call can
    return -1 with errno set to EINTR, or with errno set to EAGAIN (EWOULDBLOCK). These
    results must be properly managed (not done properly above). If your program is not going
    to receive any signals, then it is unlikely you will get EINTR. If your program does not
    set nonblocking I/O, you will not get EAGAIN.

    8. Never call read(2), recv(2), write(2), or send(2) with a buffer length of zero.

    9. If the functions read(2), recv(2), write(2), and send(2) fail with errors other than
    those listed in 7., or one of the input functions returns 0, indicating end of file, then
    you should not pass that descriptor to select() again. In the example below, I close the
    descriptor immediately, and then set it to -1 to prevent it being included in a set.

    10. The timeout value must be initialized with each new call to select(), since some operat-
    ing systems modify the structure. pselect() however does not modify its timeout struc-
    ture.

    11. Since select() modifies its file descriptor sets, if the call is being used in a loop,
    then the sets must be reinitialized before each call.
    Last edited by nonpuz; 06-26-2011 at 05:50 AM.

  3. #3
    Registered User
    Join Date
    Mar 2008
    Location
    India
    Posts
    147
    Thank you very much for your detailed comments, it is really very very informative.

  4. #4
    Registered User
    Join Date
    Mar 2008
    Location
    India
    Posts
    147
    nonpuz,

    what i grasp here is following points

    1) the server sockfd would be read set when select call comes out from waiting .

    2) we compare from all the fds our sockfd and then accept the connection to recive data on that connection from the new fd.

    3) we add that client fd to the readset ,now in the for loop when the fd is not of server fd it will goto else case and read the data from that fd.

    another confustion here is in select system call we say testfds is for reading i.e if somebody is writing on to that we would be getting out of the select system call to read the data hope iam right here

    i.e the client is writing on to the server sock fd .

    here accept system call would accept the connection and give fd , so total 2 fds one is server socket fd and then accept result client fd am i right?

    in that case client is writing through the client sock fd , and server is reading throught server sock fd .

    is my conclustions are right

    thanks for your detailed comments and expecting the your comments on above.

  5. #5
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by vlrk View Post
    nonpuz,

    what i grasp here is following points

    1) the server sockfd would be read set when select call comes out from waiting.
    It might be. If it was read set before, but there are no connections waiting on the server (aka, listen) socket, it will not be. The only purpose of the listen socket is to accept() connections. If the listen socket is ready to read, you call accept() on it. Do not actually write() or read() on it.

    2) we compare from all the fds our sockfd and then accept the connection to recive data on that connection from the new fd.
    Beware that you have to keep a list separate from the fdset you submit to select(), because select will alter it. Also, you cannot find out what's in an fdset easily -- you can only ask if a particular fd ISSET. So you need a separate array of ints that you are going to check for when select() returns.

    3) we add that client fd to the readset ,now in the for loop when the fd is not of server fd it will goto else case and read the data from that fd.
    Yes.

    another confustion here is in select system call we say testfds is for reading i.e if somebody is writing on to that
    I would not be passing the fds on for concurrent operations and keeping them in the select() call, that is just going to create a mess. If you want to write to a client, remove it from the readset until you are done writing.

    i.e the client is writing on to the server sock fd .
    The only reason a client will be waiting on the server listen socket is because it is not yet connected. After that it waits on its own socket. Clients cannot write data to the listen socket.

    here accept system call would accept the connection and give fd , so total 2 fds one is server socket fd and then accept result client fd am i right?
    Yes.

    in that case client is writing through the client sock fd , and server is reading throught server sock fd .
    Wrong. After accept(), the server reads from and writes to the client via the same socket, the new one returned by accept(). Again: never read() or write() to the listening socket. There is no one there to read or write to.
    Last edited by MK27; 06-26-2011 at 08:50 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Doubt on usage of static...
    By sanddune008 in forum C Programming
    Replies: 7
    Last Post: 05-11-2009, 08:09 AM
  2. select system call with FIFO fd.
    By vlrk in forum Linux Programming
    Replies: 0
    Last Post: 05-11-2009, 04:27 AM
  3. Replies: 6
    Last Post: 07-21-2008, 06:04 AM
  4. simultaneously waiting for data on FIFO and UDP using select call
    By yogesh3073 in forum Networking/Device Communication
    Replies: 2
    Last Post: 01-05-2007, 09:53 AM
  5. Select Menu and Call Function
    By studentInC in forum C Programming
    Replies: 17
    Last Post: 12-14-2002, 07:42 PM