C Board  

Go Back   C Board > General Programming Boards > Networking/Device Communication

Reply
 
LinkBack Thread Tools Display Modes
Old 04-06-2009, 02:27 PM   #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
redcameron is offline   Reply With Quote
Old 04-06-2009, 02:32 PM   #2
+++ OK NO CARRIER
 
quzah's Avatar
 
Join Date: Oct 2001
Posts: 10,643
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.
__________________
Hundreds of thousands of dipshits can't be wrong.


Are you up for the suck?
quzah is offline   Reply With Quote
Old 04-06-2009, 03:09 PM   #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...
redcameron is offline   Reply With Quote
Old 04-06-2009, 03:37 PM   #4
Senior software engineer
 
brewbuck's Avatar
 
Join Date: Mar 2007
Location: Portland, OR
Posts: 5,768
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.
__________________
"Congratulations on your purchase. To begin using your quantum computer, set the power switch to both off and on simultaneously." -- raftpeople@slashdot
brewbuck is offline   Reply With Quote
Old 04-06-2009, 03:48 PM   #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.
redcameron is offline   Reply With Quote
Old 04-06-2009, 04:27 PM   #6
Senior software engineer
 
brewbuck's Avatar
 
Join Date: Mar 2007
Location: Portland, OR
Posts: 5,768
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?
__________________
"Congratulations on your purchase. To begin using your quantum computer, set the power switch to both off and on simultaneously." -- raftpeople@slashdot
brewbuck is offline   Reply With Quote
Old 04-06-2009, 05:03 PM   #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.
redcameron is offline   Reply With Quote
Old 04-06-2009, 05:26 PM   #8
Senior software engineer
 
brewbuck's Avatar
 
Join Date: Mar 2007
Location: Portland, OR
Posts: 5,768
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.
__________________
"Congratulations on your purchase. To begin using your quantum computer, set the power switch to both off and on simultaneously." -- raftpeople@slashdot
brewbuck is offline   Reply With Quote
Old 04-06-2009, 05:34 PM   #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.
redcameron is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

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


All times are GMT -6. The time now is 04:02 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22