Thread: Not coming out of select()loop after ctrl-c on client

  1. #1
    Registered User
    Join Date
    Jul 2011
    Posts
    99

    Not coming out of select()loop after ctrl-c on client

    I am experimenting with the select() statement. I copied an example from Beej Networking Guide and adapted it a little. The following happens:
    (1) start up server and then the client, every thing fine
    (2) if the client sends a message: everything fine, the server prints it
    (3) if I gracefully shut down the client, the server neatly prints out the message "socket .... hung up"
    (4) if I exit the client non-gracefully by giving it ctrl-c the server goes into a loop and keeps on printing the last received data and goes into the last for loop over and over again, it seems to think that it is still receiving data from the client

    (everything runs on a dos box on windows xp)

    QUESTION1: Does anybody know what is happening here? Why does it keep coming back to the red part?
    QUESTION 2: when looping through the sockets by going do fdmax which contains the highest socket number which can be high (e.g. for the second connected socket 1890) it goes through all number below even if those numbers do not represent an existing socket number, is this logical? Would it not be more logical to represent the connect sockets via a linked list?

    Code:
        // main loop
        for(;;) {
            read_fds = master; // copy it
            if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
                perror("select");
                exit(4);
            }
    
            // run through the existing connections looking for data to read
            for(i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // we got one!!
                    if (i == welcome_s) {
                        // handle new connections
                        addr_len = sizeof client_addr;
    					connect_s = accept(welcome_s,
    						(struct sockaddr *)&client_addr,
    						&addr_len);
    
    					if (connect_s == 0) {
                            perror("accept");
                        } else {
                            FD_SET(connect_s, &master); // add to master set
                            if (connect_s > fdmax) {    // keep track of the max
    							fdmax = connect_s +1;
                            }
    						// Print an informational message that accept completed
                            if (send(connect_s, "Welcome!", sizeof("Welcome!"), 0) == -1) {
    							perror("send");
                            }
    						printf("Accept completed (IP address of client = %s  port = %d) \n",
    						inet_ntoa(client_ip_addr), ntohs(client_addr.sin_port));
    					}
                    } else {
                        // handle data from a client
                        if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
                            // got error or connection closed by client
                            if (nbytes == 0) {
                                // connection closed
                                printf("selectserver: socket %d hung up\n", i);
                            } else {
                                perror("recv");
                            }
                            closesocket(i); // bye!
                            FD_CLR(i, &master); // remove from master set
                        } else {
    
                            // we got some data from a client
                            for(j = 0; j <= fdmax; j++) {
                                // send to everyone!
                                if (FD_ISSET(j, &master)) {
                                    // except the welcome_s and ourselves
                                    if (j != welcome_s && j != i) {
                                        if (send(j, buf, nbytes, 0) == -1) {
                                            perror("send");
                                        }
                                    }
                                }
                            }
    						printf("We got some data: %s, fdmax: %i!\n", buf, fdmax);
                        }
                    } // END handle data from client
    
                } // END got new incoming connection
            } // END looping through file descriptors
        } // END for(;;)--and you thought it would never end!
    Last edited by django; 08-15-2011 at 02:49 PM.

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    This would be much better suited for the Networking/Device forum.

    Quote Originally Posted by django View Post
    I am experimenting with the select() statement. I copied an example from Beej Networking Guide and adapted it a little.
    What example? Can you provide a section/page number? Nothing immediate jumps out at me in the code you provided. A copy of your full server and client programs would be useful so I can run them myself and test/debug.

    (3) if I gracefully shut down the client, the server neatly prints out the message "socket .... hung up"
    How exactly do you gracefully shut down the client (this may well be answered by providing client code)

    (everything runs on a dos box on windows xp)
    Why? Any reason you're using a virtualized 20 year old OS on top of a nearly 10 year old OS? Any reason you can't simply write this for Windows (there are free compilers) or a VM running Linux?

    QUESTION1: Does anybody know what is happening here? Why does it keep coming back to the red part?
    Not yet, I don't. Why are you using read_fds and master? Why not just use master since you never change read_fds? I also don't know if you can safely copy an fd_set using the = sign. Can you give us some actual output (cut and paste from your terminal)?

    QUESTION 2: when looping through the sockets by going do fdmax which contains the highest socket number which can be high (e.g. for the second connected socket 1890) it goes through all number below even if those numbers do not represent an existing socket number, is this logical? Would it not be more logical to represent the connect sockets via a linked list?
    I find that highly unlikely, or perhaps you have a serious bug. Perhaps you mean port 1890. The socket number should be a relatively low integer corresponding to a file descriptor. Normally, stdin, stdout and stderr are 0, 1 and 2. Anything you open after that should be roughly sequential. If you truly have socket descriptors in the 1800's, you've opened way too many sockets. DOS usually limited people to ~40 file descriptors, IIRC, but I never did any network programming in DOS, so I can't say how that limit applies to sockets.

  3. #3
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by django View Post
    QUESTION 2: when looping through the sockets by going do fdmax which contains the highest socket number which can be high (e.g. for the second connected socket 1890) it goes through all number below even if those numbers do not represent an existing socket number, is this logical?
    Code:
            // run through the existing connections looking for data to read
            for(i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // we got one!!
    Why wouldn't it be logical? It's doing exactly what you are telling it to do. Start at zero, and count by one up to fdmax, and then test to see if it's set. It's doing exactly what you told it to do.


    Quzah.
    Hope is the first step on the road to disappointment.

  4. #4
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Quote Originally Posted by anduril462 View Post
    This would be much better suited for the Networking/Device forum.
    ok, I will keep that in mind for next time.
    Quote Originally Posted by anduril462 View Post
    What example? Can you provide a section/page number? Nothing immediate jumps out at me in the code you provided. A copy of your full server and client programs would be useful so I can run them myself and test/debug.
    this example (second one, the longer one)

    Quote Originally Posted by anduril462 View Post
    How exactly do you gracefully shut down the client (this may well be answered by providing client code)
    See code below (but also the comment below the code, it is not really relevant anymore)


    (everything runs on a dos box on windows xp)
    Quote Originally Posted by anduril462 View Post
    Why? Any reason you're using a virtualized 20 year old OS on top of a nearly 10 year old OS? Any reason you can't simply write this for Windows (there are free compilers) or a VM running Linux?
    I might be mistaken. I run it in the window that you get when you run cmd.exe. Judging from what it says, this is probably just running on windows XP. We used to call it a DOS box, hence my mistake.
    Quote Originally Posted by anduril462 View Post
    Not yet, I don't. Why are you using read_fds and master? Why not just use master since you never change read_fds? I also don't know if you can safely copy an fd_set using the = sign. Can you give us some actual output (cut and paste from your terminal)?
    The explanation (which seems logical to me) also from the article above: "The reason I have the master set is that select() actually changes the set you pass into it to reflect which sockets are ready to read. Since I have to keep track of the connections from one call of select() to the next, I must store these safely away somewhere. At the last minute, I copy the master into the read_fds, and then call select().". So contrary to what you say, Beej is stating that you actually do change the set which seems logical to me, how would you otherwise know what is changed (= has received data) after using select?
    Quote Originally Posted by anduril462 View Post
    I find that highly unlikely, or perhaps you have a serious bug. Perhaps you mean port 1890. The socket number should be a relatively low integer corresponding to a file descriptor. Normally, stdin, stdout and stderr are 0, 1 and 2. Anything you open after that should be roughly sequential. If you truly have socket descriptors in the 1800's, you've opened way too many sockets. DOS usually limited people to ~40 file descriptors, IIRC, but I never did any network programming in DOS, so I can't say how that limit applies to sockets.
    run the server code below, there is a printf that prints the socketnumber and it is often around 1900!
    Code:
    Here is the server code:
    
    #define  WIN                // WIN for Winsock and BSD for BSD sockets
    
    //----- Include files ---------------------------------------------------------
    #include <stdio.h>          // Needed for printf()
    #include <string.h>         // Needed for memcpy() and strcpy()
     #include <winsock.h>      // Needed for all Winsock stuff
    
    //----- Defines ---------------------------------------------------------------
    #define  PORT_NUM   1050    // Arbitrary port number for the server
    
    //===== Main program ==========================================================
    int main(void)
    {
      WORD wVersionRequested = MAKEWORD(1,1);       // Stuff for WSA functions
      WSADATA wsaData;                              // Stuff for WSA functions
      unsigned int         welcome_s;       // Welcome socket descriptor
      struct sockaddr_in   server_addr;     // Server Internet address
      unsigned int         connect_s;       // Connection socket descriptor
      struct sockaddr_in   client_addr;     // Client Internet address
      struct in_addr       client_ip_addr;  // Client IP address
      int                  addr_len;        // Internet address length
      char                 buf[100];    // Output buffer for data
    
      // This stuff initializes winsock
      WSAStartup(wVersionRequested, &wsaData);
    
      // >>> Step #1 <<<
      // Create a welcome socket
      //   - AF_INET is Address Family Internet and SOCK_STREAM is streams
      welcome_s = socket(AF_INET, SOCK_STREAM, 0);
    
      // >>> Step #2 <<<
      // Fill-in server (my) address information and bind the welcome socket
      //   - See winsock.h for a description of struct sockaddr_in
      server_addr.sin_family = AF_INET;                 // Address family to use
      server_addr.sin_port = htons(PORT_NUM);           // Port number to use
      server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // Listen on any IP address
      bind(welcome_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
    
      // >>> Step #3 <<<
      // Listen on welcome socket for a connection
      listen(welcome_s, 1);
    
    //----------------------SELECT------------------------------- 
    
    	unsigned int fdmax;        // maximum file descriptor number
    
        fd_set master;    // master file descriptor list
        fd_set read_fds;  // temp file descriptor list for select()
    
        FD_ZERO(&master);    // clear the master and temp sets
        FD_ZERO(&read_fds);
    
    	FD_SET(welcome_s, &master);
    	fdmax = welcome_s;
    	printf("FDMAX: %i", fdmax);
    	unsigned int i, j, nbytes;
    
        // main loop
        for(;;) {
            read_fds = master; // copy it
            if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
                perror("select");
                exit(4);
            }
    
            // run through the existing connections looking for data to read
            for(i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // we got one!!
                    if (i == welcome_s) {
                        // handle new connections
                        addr_len = sizeof client_addr;
    					connect_s = accept(welcome_s,
    						(struct sockaddr *)&client_addr,
    						&addr_len);
    
    					if (connect_s == 0) {
                            perror("accept");
                        } else {
                            FD_SET(connect_s, &master); // add to master set
                            if (connect_s > fdmax) {    // keep track of the max
    							fdmax = connect_s +1;
                            }
    						// Print an informational message that accept completed
                            if (send(connect_s, "Welcome!", sizeof("Welcome!"), 0) == -1) {
    							perror("send");
                            }
    						printf("Accept completed (IP address of client = %s  port = %d) \n",
    						inet_ntoa(client_ip_addr), ntohs(client_addr.sin_port));
    					}
                    } else {
                        // handle data from a client
                        if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
                            // got error or connection closed by client
                            if (nbytes == 0) {
                                // connection closed
                                printf("selectserver: socket %d hung up\n", i);
                            } else {
                                perror("recv");
                            }
                            closesocket(i); // bye!
                            FD_CLR(i, &master); // remove from master set
                        } else {
    					printf("BYTES: %i\n", nbytes);
                            // we got some data from a client
                            for(j = 0; j <= fdmax; j++) {
                                // send to everyone!
                                if (FD_ISSET(j, &master)) {
                                    // except the welcome_s and ourselves
                                    if (j != welcome_s && j != i) {
                                        if (send(j, buf, nbytes, 0) == -1) {
                                            perror("send");
                                        }
                                    }
                                }
                            }
    						printf("We got some data: %s, fdmax: %i!\n", buf, fdmax);
                        }
                    } // END handle data from client
                } // END got new incoming connection
            } // END looping through file descriptors
        } // END for(;;)--and you thought it would never end!
    
    
    //----------------------END SELECT------------------------------- 
    
    
    // >>> Step #7 <<<
    // Close the welcome and connect sockets
      // Clean-up winsock
      WSACleanup();
    }
    
    
    And here is the client code:
    
    
    //----- Include files ---------------------------------------------------------
    #include <stdio.h>          // Needed for printf()
    #include <string.h>         // Needed for memcpy() and strcpy()
    #include <winsock.h>      // Needed for all Winsock stuff
    
    //----- Defines ---------------------------------------------------------------
    #define  PORT_NUM         1050   // Port number used at the server
    #define  IP_ADDR "127.0.0.1"  // IP address of server (*** HARDWIRED ***)
    
    //===== Main program ==========================================================
    void main(int argc, char *argv[]){
    	WORD wVersionRequested = MAKEWORD(1,1);       // Stuff for WSA functions
    	WSADATA wsaData;                              // Stuff for WSA functions
    	unsigned int         client_s;        // Client socket descriptor
    	struct sockaddr_in   server_addr;     // Server Internet address
    	char                 out_buf[100];    // Output buffer for data
    	char                 in_buf[100];     // Input buffer for data
    
    	// This stuff initializes winsock
    	WSAStartup(wVersionRequested, &wsaData);
    
    	// >>> Step #1 <<<
    	// Create a client socket
    	//   - AF_INET is Address Family Internet and SOCK_STREAM is streams
    	client_s = socket(AF_INET, SOCK_STREAM, 0);
    
    
    	server_addr.sin_addr.s_addr = inet_addr(IP_ADDR); // IP address to use
    
    
    	// >>> Step #2 <<<
    	// Fill-in the server's address information and do a connect with the
    	// listening server usingh the client socket.  The connect() will block.
    	server_addr.sin_family = AF_INET;                 // Address family to use
    	server_addr.sin_port = htons(PORT_NUM);           // Port num to use
    	//  server_addr.sin_addr.s_addr = inet_addr(IP_ADDR); // IP address to use
    	connect(client_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
    
    	// >>> Step #3 <<<
    	// Receive from the server using the client socket
    	recv(client_s, in_buf, sizeof(in_buf), 0);
    	printf("Received from server... data = '%s' \n", in_buf);
    
    
    	// >>> Step #4 <<<
    	// Send to the server using the client socket
    	strcpy(out_buf, "Test message from client to server");
    	send(client_s, out_buf, (strlen(out_buf) + 1), 0);
    
    	char c;
    	char mess[100];
    	int q = 0;
    	
    	while (1) {
    		while ((c = getchar()) != '\n') {
    			mess[q++] = c;
    		}
    		mess[q] = '\0';
    		q = 0;
    		if (strcmp(mess, "stop") == 0) 
    			break;
    		strcpy(out_buf, mess);
    		send(client_s, out_buf, (strlen(out_buf) + 1), 0);
    	}
    
    	// >>> Step #5 <<<
    	// Close the client socket
    	closesocket(client_s);
    	// Clean-up winsock
    	WSACleanup();
    }
    I have discovered a little more.
    If you look at the declaration of nbytes (firstline in red) it is an unsigned integer, causing the 'if statement' never to be true (second red line). I have declared nbytes as an integer and the problem of the eternal loop is over. (try it and execute beforeand after and you will see what I mean). Btw declaring with unsigned integers has to do with the fact that in winsock, some of the BSD functions have slightly different return values. Basically, a function like recv() just is not portable if you handle the errorcodes under windows.
    Last edited by django; 08-15-2011 at 05:23 PM.

  5. #5
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Quote Originally Posted by quzah View Post
    Why wouldn't it be logical? It's doing exactly what you are telling it to do. Start at zero, and count by one up to fdmax, and then test to see if it's set. It's doing exactly what you told it to do.


    Quzah.
    It is not that it does not do what it is supposed to do, it is that I do not really understand the logic of it.
    The set contains the sockets which are simply represented by integers. Now suppose that you want to loop through the available sockets and the set contains the following numbers [1953, 2011], then the loop starts running up from 0, goes through the whole darn thing 1953 times until it encounters the first actual socket number. This seems highly inefficient.

  6. #6
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by django View Post
    It is not that it does not do what it is supposed to do, it is that I do not really understand the logic of it.
    The set contains the sockets which are simply represented by integers. Now suppose that you want to loop through the available sockets and the set contains the following numbers [1953, 2011], then the loop starts running up from 0, goes through the whole darn thing 1953 times until it encounters the first actual socket number. This seems highly inefficient.
    You're right, it's not terribly efficient. But there's no rule that says you must start at 0, nor is there a rule that says you can't track the minimum socket fd yourself, or keep your own linked list. The fd_set structure may indeed use a linked list internally, but it's designed to be opaque, so you don't know or care how it's implemented. If you want to only check the handful of socket descriptors you have, which seem to be clustered around the 1800-2000 mark, then you need to keep your own list of them, or at least track the minimum:
    Code:
    // fdmin is tracked similar to how you track fdmax
    for (j = fdmin; j <= fdmax; j++)
        // your code here

  7. #7
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by django View Post
    I might be mistaken. I run it in the window that you get when you run cmd.exe. Judging from what it says, this is probably just running on windows XP. We used to call it a DOS box, hence my mistake.
    Ahh, yes, I misread/misunderstood. I was thinking you meant DOSBox - Wikipedia, the free encyclopedia, not simply a console program (the console app in Windows being called a DOS box). That makes more sense.

    The explanation (which seems logical to me) also from the article above: "The reason I have the master set is that select() actually changes the set you pass into it to reflect which sockets are ready to read. Since I have to keep track of the connections from one call of select() to the next, I must store these safely away somewhere. At the last minute, I copy the master into the read_fds, and then call select().". So contrary to what you say, Beej is stating that you actually do change the set which seems logical to me, how would you otherwise know what is changed (= has received data) after using select?
    Duh. Seems obvious now.

    run the server code below, there is a printf that prints the socketnumber and it is often around 1900!
    Something I never knew. I program on Linux. The Winsock API is very similar to that of the Linux/BSD sockets library, but I guess Windows hands out descriptors differently.

    I have discovered a little more.
    If you look at the declaration of nbytes (firstline in red) it is an unsigned integer, causing the 'if statement' never to be true (second red line). I have declared nbytes as an integer and the problem of the eternal loop is over. (try it and execute beforeand after and you will see what I mean). Btw declaring with unsigned integers has to do with the fact that in winsock, some of the BSD functions have slightly different return values. Basically, a function like recv() just is not portable if you handle the errorcodes under windows.
    [/quote]
    Good catch. And another Windows/Linux difference worth noting.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. End a loop using Ctrl-z
    By GibsMe in forum C Programming
    Replies: 4
    Last Post: 10-12-2009, 04:06 AM
  2. problem in select() in a Multiprotocol client
    By zealot in forum Networking/Device Communication
    Replies: 2
    Last Post: 04-09-2009, 12:45 PM
  3. help coming up with loop/algorithm
    By Fredir in forum C++ Programming
    Replies: 7
    Last Post: 12-11-2007, 02:58 PM
  4. Using select() for client UDP
    By jazzman83 in forum C Programming
    Replies: 2
    Last Post: 04-03-2007, 05:31 AM
  5. Need winsock select() client & server source code or tutorial
    By draggy in forum Networking/Device Communication
    Replies: 2
    Last Post: 06-19-2006, 11:49 AM