Thread: Multiplexing read socket and stdin

  1. #1
    Registered User
    Join Date
    Apr 2009
    Posts
    9

    Multiplexing read socket and stdin

    Hi there,

    I'm having some trouble doing some multiplexing with my script. I'm trying to wait on data either from the socket or stdin but for some reason my code keeps blocking right at the select statement. I think the code needs to be looked at by fresh (and more experienced) eyes.

    Here's my code to connect to the server:

    Code:
    static void connect_to_server() {
        struct hostent *hp;
        struct sockaddr_in r;
        
        if ((hp = gethostbyname(host)) == NULL) {
    	    fprintf(stderr, "%s: no such host\n", host);
     	    exit(1);
        }
        if (hp->h_addr_list[0] == NULL || hp->h_addrtype != AF_INET) {
    	    fprintf(stderr, "%s: not an internet protocol host name\n", host);
    	    exit(1);
        }
        if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            exit(1);
        }
    	memset((char *) &r, '\0', sizeof r);
        r.sin_family = AF_INET;
        memcpy((char *) &r.sin_addr, hp->h_addr_list[0], hp->h_length);
        r.sin_port = htons(port);
    	
        if (connect(serverfd, (struct sockaddr *) &r, sizeof r) < 0) {
            perror("connect");
            exit(1);
        }
        // Establish file descriptor lists for stdin and server reading.  
        FD_ZERO(&fdlist);
        FD_SET(serverfd, &fdlist);
        FD_SET(fileno(stdin), &fdlist);
    }
    Here's my select statement:

    Code:
    static void do_something() {
        printf("do something\n");
        char *q;
        if ((q = memnewline(buf, bytes_in_buf))) {
            do_something_server(q);
        } else {
            printf("do switch %d\n", serverfd);
            switch (select(serverfd + 1, &fdlist, NULL, NULL, NULL)) {
                case 0:
                    printf("SELECT RETURN 0\n");
                    break;
                case -1:
                    perror("select");
                    break;
                default:
                    printf("fdisset\n");
                    if (FD_ISSET(fileno(stdin), &fdlist)) {
                        if (! fgets(buf, sizeof buf, stdin)) {
                            if (ferror(stdin)) {
                                perror("stdin");
                                exit(1);
                            }
                        }
                        char **cmd_options = explode(buf);
                        docmd(cmd_options);
                    } else if (FD_ISSET(serverfd, &fdlist)) {
                        printf("server\n");
                        int n;
                        if ((n = read(serverfd,
                                    buf + bytes_in_buf,
                                    sizeof(buf) - bytes_in_buf)) == -1) {
                            perror("read");
                            exit(1);
                        }
                        bytes_in_buf += n;
                    }
            }
        }
    }
    Basically the script manages to read all the incoming data from the server, but once no more is being sent the script outputs "do switch 3" and sits. This is how I know that it's just hanging at that point.

    It's seriously ........ing me off. Heh.

    Thanks in advance,

    Dave

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You can't get the input stream to not-block with anything standard (that's why there's no standard 'getch'). Stop trying to read from stdin and see if it starts working.


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

  3. #3
    Registered User
    Join Date
    Apr 2009
    Posts
    9
    I commented out the entire fgets part and still nothing.

    I'm not really understanding what you mean...heh. Can we simply it a bit? I don't want to not-block for stdin, but when I give it data it doesn't react...

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    fgets() attempts to read an entire line of input. If it does not see a newline, it will block. Your select() is probably working correctly -- there is SOME data available, but not enough for an entire line.

    You need to move away from using fgets() and input the data directly with fread(). If an entire line is not yet available, you need to loop again and wait for the rest of it.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    Registered User
    Join Date
    Apr 2009
    Posts
    9
    Hmmm, it *does* seem to be blocking on select() because I put a printf() on my default switch case in case it reaches that point.

    I'm using fgets to retrieve data that WILL have a new line no matter what. So that's definitely not the issue either.

  6. #6
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by redcameron View Post
    Hmmm, it *does* seem to be blocking on select() because I put a printf() on my default switch case in case it reaches that point.

    I'm using fgets to retrieve data that WILL have a new line no matter what. So that's definitely not the issue either.
    If select() is blocking then no data is ready. What are you expecting?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  7. #7
    Registered User
    Join Date
    Apr 2009
    Posts
    9
    Sorry of this sounds rude, I appreciate the help, but maybe you should re-read what I'm trying to accomplish here. I'm writing a small game application for the command-line which either reacts to server input OR to user input. At the moment I'm trying to give it input but it blocks indefinitely and doesn't reach the point where I'm doing my fgets().

    Here is a much simpler and full piece of code I've written and am now testing with:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <netdb.h>
    
    static char *host = "localhost";
    static int port = 1234;
    
    int serverfd;
    int bytes_in_buf = 0;
    char buf[200];
    
    static void connect_to_server();
    static void do_something();
    char *memnewline(char *p, int size);
    static void clear_buf();
    static fd_set fdlist;
    static void do_handle_setup();
    
    int main(int argc, char **argv) {
        connect_to_server();
    	do_handle_setup();
    	clear_buf();
        while (1) {
            do_something();
        }
        return 0;
    }
    
    static void clear_buf() {
        memset(buf, '\0', sizeof(buf));
        bytes_in_buf = 0;
    }
    
    static void connect_to_server() {
        struct hostent *hp;
        struct sockaddr_in r;
        
        if ((hp = gethostbyname(host)) == NULL) {
            fprintf(stderr, "%s: no such host\n", host);
             exit(1);
        }
        if (hp->h_addr_list[0] == NULL || hp->h_addrtype != AF_INET) {
            fprintf(stderr, "%s: not an internet protocol host name\n", host);
            exit(1);
        }
        if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            exit(1);
        }
        memset((char *) &r, '\0', sizeof r);
        r.sin_family = AF_INET;
        memcpy((char *) &r.sin_addr, hp->h_addr_list[0], hp->h_length);
        r.sin_port = htons(port);
        
        if (connect(serverfd, (struct sockaddr *) &r, sizeof r) < 0) {
            perror("connect");
            exit(1);
        }
        int n;
        while (memnewline(buf, bytes_in_buf) == NULL) {
            if ((n = read(serverfd,
                          buf + bytes_in_buf,
                          sizeof(buf) - bytes_in_buf)) == -1) {
                perror("read");
                exit(1);
            }
            bytes_in_buf += n;
        }
        // Establish file descriptor lists for stdin and server reading.  
        FD_ZERO(&fdlist);
        FD_SET(serverfd, &fdlist);
        FD_SET(fileno(stdin), &fdlist);
    }
    
    static void send_to_server(char *data) {
        if (write(serverfd, data, strlen(data)) != strlen(data)) {
            perror("write");
    		exit(1);
        }
    }
    
    static void do_handle_setup() {
        printf("%s\n", "Give handle:\n");
        fgets(buf, sizeof(buf), stdin);
        send_to_server(buf);
    }
    
    static void do_something() {
        char *q;
        if ((q = memnewline(buf, bytes_in_buf))) {
            printf("server: %s\n", buf);
    		clear_buf();
        } else {
    		printf("select\n");
            switch (select(serverfd + 1, &fdlist, NULL, NULL, NULL)) {
                case 0:
                    break;
                case -1:
                    perror("select");
                    break;
                default:
                    if (FD_ISSET(fileno(stdin), &fdlist)) {
                        if (! fgets(buf, sizeof buf, stdin)) {
                            if (ferror(stdin)) {
                                perror("stdin");
                                exit(1);
                            }
                        }
                    } else if (FD_ISSET(serverfd, &fdlist)) {
                        int n;
                        if ((n = read(serverfd,
                                      buf + bytes_in_buf,
                                      sizeof(buf) - bytes_in_buf)) == -1) {
                            perror("read");
                            exit(1);
                        }
                        bytes_in_buf += n;
                    }
            }
        }
    }
    
    char *memnewline(char *p, int size)  /* finds \r _or_ \n */
    /* This is like min(memchr(p, '\r'), memchr(p, '\n')) */
    /* It is named after memchr().  There's no memcspn(). */
    {
        for (; size > 0; p++, size--)
    		if (*p == '\r' || *p == '\n')
    			return(p);
        return(NULL);
    }
    Basically the memnewline() function is used to determine when a \r\n is found then the buf is cleared.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Again, if select() is blocking, there is no data available on the fd. You could be running into a buffering issue where stdin has already been read into the buffer, but no data is immediately waiting on the fd.

    Try putting stdin into non-buffered mode and see if that helps. If not, then there is no data available, even if you think there should be.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    Registered User
    Join Date
    Apr 2009
    Posts
    9
    No you're still not understanding the issue. Either way, I've resolved it. If you're wondering what was wrong, I was setting my masks in the wrong place, select apparently modifies the list every time so they need to be reset on every iteration - something I failed to realise earlier.

    This is what fixed it:

    Code:
        while (1) {
    		// Establish file descriptor lists for stdin and server reading.  
    		FD_ZERO(&fdlist);
    		FD_SET(serverfd, &fdlist);
    		FD_SET(fileno(stdin), &fdlist);
            do_something();
        }
    Cheers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. socket programming question, closing sockets...
    By ursula in forum Networking/Device Communication
    Replies: 2
    Last Post: 05-31-2009, 05:17 PM
  2. Help With Socket Programming
    By namasteall2000 in forum Networking/Device Communication
    Replies: 8
    Last Post: 02-17-2009, 12:58 PM
  3. socket error
    By xlordt in forum Networking/Device Communication
    Replies: 2
    Last Post: 09-25-2003, 12:39 AM
  4. STDIN stdout
    By GreyMattr in forum Linux Programming
    Replies: 2
    Last Post: 08-01-2002, 01:29 PM