1

I have a client and server, and the client runs a select loop to multiplex between a TCP and a UDP connection. I'm trying to add my TCP connection file descriptor to both the read and the write set and then initiate one message exchange using write set and one using read set. My message communication with the write set works fine but with the read set I'm unable to do so.

Client Code:

    char buf[256] = {};
    char buf_to_send[256] = {};
    int nfds, sd, r;
    fd_set rd, wr;
    int connect_init = 1;

/* I do the Connect Command here */

    FD_ZERO(&rd);
    FD_ZERO(&wr);

    FD_SET(sd, &rd);
    FD_SET(sd, &wr);

    nfds = sd;


    for(; ;){

       r = select(nfds + 1, &rd, &wr, NULL, NULL);

       if(connect_init == 0){

          if(FD_ISSET(sd, &rd)){    // this is not working, if I change rd to wr, it works!

          r = recv(sd, buf, sizeof(buf),0);
          printf("received buf  = %s", buf);
          sprintf(buf, "%s", "client_reply\n");
          send(sd, buf, strlen(buf), 0);

      }
   }
/* Everything below this works correctly */
if (connect_init){

   if(FD_ISSET(sd, &wr)){

     sprintf(buf_to_send, "%s", "Client connect request");
     write(sd, buf_to_send, strlen(buf_to_send)); 

     recv(sd, buf, sizeof(buf), 0);
     printf("Server said = %s", buf);  

     sprintf(buf_to_send, "Hello!\n"); // client Hellos back
     send(sd, buf_to_send, strlen(buf_to_send), 0);

   }
   connect_init = 0;
  }

} // for loops ends
tshepang
  • 12,111
  • 21
  • 91
  • 136
Zahaib Akhtar
  • 1,068
  • 2
  • 11
  • 26

1 Answers1

5

You need to initialize the sets in the loop, every time before calling select. This is needed because select modifies them. Beej's Guide to Network Programming has a comprehensive example on one way to use select.

So in your code, it seems select returns first with writing allowed, but reading not, which has the read bit reset to 0, and then there's nothing to set it back to 1, because from then on select will not touch it, because it is already 0.

If select API bothers you, look at poll, it avoids this (note that there's probably no practical/efficiency difference, it basically boils down to personal preference). On a "real" code with many descriptors (such as a network server with many clients), where performance matters, you should use some other mechanism though, probably some higher level event library, which then uses the OS specific system API, such as Linux's epoll facility. But checking just a few descriptors, select is the tried and true and relatively portable choice.

Dmitry
  • 2,989
  • 2
  • 24
  • 34
hyde
  • 60,639
  • 21
  • 115
  • 176
  • do you mean I should move my `fd_set rd, wr;` declaration inside the loop? – Zahaib Akhtar Nov 03 '13 at 07:02
  • 1
    Not necessarily, but certainly the zeroing and setting. – user207421 Nov 03 '13 at 07:04
  • @ZahaibAkhtar That's the simplest, do that and see if it works. – hyde Nov 03 '13 at 07:04
  • Right, so the declaration can remain outside, but zeroing and setting goes inside! yep, trying that in my code now. – Zahaib Akhtar Nov 03 '13 at 07:05
  • Yes, its working just fine now. Could you please outline why this is a necessity if possible, it will make me understand. Or maybe point me to a relevant reading. Thanks! Oh I see, because `select` modifies them. – Zahaib Akhtar Nov 03 '13 at 07:07
  • Added a sentence to the answer. I'm not actually 100% sure (I've mostly used `poll`, so would have to do some research on `select` to be sure) if calling `FD_ZERO` is really necessary in your case or not, that is is there any risk of `select` having set some unspecified bits to 1 or are they guaranteed to remain 0. – hyde Nov 03 '13 at 07:12
  • Okay, yeah I just ran a quick test and moved `FD_ZERO` out of the for loop, still works fine. I guess it has to do with the `FD_SET`. If I'm not making a copy then I better set them again, because as you said `select` modifies them I guess. – Zahaib Akhtar Nov 03 '13 at 07:16
  • 2
    The reason is that select() modifies the FD set to indicate what is ready. If you don't reset it yourself, you are only selecting on what was ready last time. – user207421 Nov 03 '13 at 07:29
  • @ZahaibAkhtar Added some stuff to answer, especially a link which you may find interesting, if you are doing more programming with `select`. – hyde Nov 03 '13 at 07:40