LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 07-02-2006, 06:33 AM   #1
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Rep: Reputation: 50
read userinput and from socket at the same time


I am trying to create a small chatclient using ncurses. I have the server up running and the client connects to it.

Now, I want to both read user input using getch() _and_ recieve data from the server using recv(). But I don't want one function to block the other.

I have no idea how to do this and keep it simple (no forking or threads). I was hoping that someone here could give me a few tips on how to approach this problem.

Regards,
 
Old 07-02-2006, 10:26 AM   #2
demon_vox
Member
 
Registered: May 2006
Location: Argentina
Distribution: SuSE 10
Posts: 173

Rep: Reputation: 30
Hi,
what you want to do is multiplexing. And for that, the common approach is to use the select system call. The select function will block your program untill it has something to read in one of the file descriptor you send it to watch. Then it will unblock your program, you can determinate which one is the one with input data to read, and you can work accordingly.

To get you start read the man page:

Code:
man select
then you can search in google for some tutorial (Sorry but I dont have any link to send you right now, and I cant send you the book I read it (it is in real paper! ) )

Cheers
 
Old 07-02-2006, 10:32 AM   #3
LogicG8
Member
 
Registered: Jun 2003
Location: Long Island, NY
Distribution: Gentoo Unstable (what a misnomer)
Posts: 380

Rep: Reputation: 30
You've got a couple of options, and you will have to choose the
best for you.

1) poll or select file descriptors
*nix provides these two functions to allow a process to wait
on events for multiple file (or socket) descriptors. The trick
is that the stdin stdout and stderr file descriptors are
always the same. So you can wait on stdin and your socket at
the same time. The file descriptor for stdin is defined in
unistd.h as STDIN_FILENO

for more information man 2 select. The man page includes a
small but very useful tutorial.

2) use SIGIO
basically you use the fcntl function to tell the kernel to
send you a signal when a file descriptor is ready to be read.
It involves the extra complication of having to catch and
handle a signal.

I prefer using the poll function.
 
Old 07-02-2006, 11:31 AM   #4
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Original Poster
Rep: Reputation: 50
Thank you both for the tips. I have used select() in the server app (followed Beej's guide to unix network programming). There I have one socket for each client and go through all checking for any data. Can I do the same with the stdin? The ncurses with its getch() is not going to be a problem?

I will check the man pages and try a little bit. Hope it works out fine.
Thanks again.
 
Old 07-02-2006, 12:48 PM   #5
mhcox
Member
 
Registered: Aug 2005
Location: Albuquerque, NM
Distribution: Fedora
Posts: 30

Rep: Reputation: 15
Quote:
Originally Posted by Ephracis
Thank you both for the tips. I have used select() in the server app (followed Beej's guide to unix network programming). There I have one socket for each client and go through all checking for any data. Can I do the same with the stdin? The ncurses with its getch() is not going to be a problem?

I will check the man pages and try a little bit. Hope it works out fine.
Thanks again.
The above solutions recommending poll/select de-multiplexing are probably your best bet.

If you decide you require making the ncurses getch() call, another possible solution is using multiple threads: one for the tty input, one for the socket, and one to process the inputs. Each input thread would have to place the data into a thread-safe queue that the third thread would read from (in effect serializing your input from the multiple sources) and process (e.g. display). Besides the higher level of complexity involved with the multi-threaded solution, you would also have to worry about whether ncurses is multi-thread safe, since you have two threads accessing the library (the getch() call by the first and the display calls by the third thread).

You probably don't have to use the getch() ncurses call, but could use an unbuffered read from STDIN and then manually do the echo processing in the ncurses window.

Finally, you could try just using a non-blocking polling strategy. Do non-blocking reads on the STDIN and socket separated by a call to a sleep system call (sleep, usleep, or nanosleep) in a while loop. This is more wasteful of CPU cycles, but since this is a client processes and most CPUs today have an abundance of CPU cycles, the wasted CPU cycles may be justified by the simpler solution. Also, the sleep call in between minimizes the amount of wasted CPU cycles.

Mike
 
Old 07-02-2006, 03:03 PM   #6
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Original Poster
Rep: Reputation: 50
Thank you all. I have decided to go with the polling technique and skip using getch() and instead process everything directly from stdin (as mhcox mentioned) that solution sounds simple and effective, which is an important goal for me.
 
Old 07-03-2006, 08:27 PM   #7
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Original Poster
Rep: Reputation: 50
I think that I am getting near now. The only problem is that I can detect input on stdin but I don't know how to get it. I guess that recv() that I use on the socket is not the way to go here.

Also it seems that I need to wait until return is pressed before FD_ISSET() returns true on stdin. I liked getch() with ncurses cause I could use the raw()-mode and get the characters directly.

Any suggestions?
 
Old 07-05-2006, 01:41 AM   #8
mhcox
Member
 
Registered: Aug 2005
Location: Albuquerque, NM
Distribution: Fedora
Posts: 30

Rep: Reputation: 15
Quote:
Originally Posted by Ephracis
I think that I am getting near now. The only problem is that I can detect input on stdin but I don't know how to get it. I guess that recv() that I use on the socket is not the way to go here.

Also it seems that I need to wait until return is pressed before FD_ISSET() returns true on stdin. I liked getch() with ncurses cause I could use the raw()-mode and get the characters directly.

Any suggestions?
You need to set your socket file descriptor to non-blocking. See http://www.manualy.sk/sock-faq/unix-socket-faq-6.html. Also, I don't see why you would be using FD_ISSET() since you shouldn't be using select(). Also check http://www.opengroup.org/onlinepubs/...ions/recv.html for info on recv(). The while loop would looks something like this:
Code:
 
int keep_running = TRUE;
int socket;
int c;
char buffer[BUF_SIZ];
 
// Open socket and set O_NONBLOCK flag.
 
while (keep_running)
{
    while (true)
    {
        if ((c = getch())!=ERR)  // there was a char from the terminal read.
            break;
        usleep(100);
        errno = 0;
        if (recv(socket, buffer, sizeof(buffer), MSG_WAITALL)!=-1 && 
            (errno==EAGAIN || errno==EWOULDBLOCK)  // received bytes from socket.
            break;
    }
    if (c==ERR)  // No terminal char so buffer must have bytes from socket
    {
         // Process socket buffer.
    }
    else  // read char from terminal.
    {
        // Process terminal char.
    }
}
Mike
 
Old 07-06-2006, 12:12 PM   #9
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Original Poster
Rep: Reputation: 50
OK. I am having a bit of trouble with your code. I have never used fnctl before. Should I use it before or after calling socket()?

The current code I have just seem to ignore data on both the socket and stdin. It just hang.

Here is my current code for the client.c:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <fcntl.h>

#include <ncurses.h>

#define MAXDATASIZE 1024
#define STDIN 0

int main(int argc, char *argv[]) {

        int sockfd, nbytes, c, keep_running = TRUE;
        char sbuf[MAXDATASIZE];
        char rbuf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr;
        fd_set read_fds;

        if (argc < 2 || argc > 4) {
                fprintf(stderr, "usage: client <hostname> [port] [nickname]\n");
                exit(1);
        }

        int port = 3067;
        if (argc > 2) {
                port = atoi(argv[2]);
                printf("using port %i\n", port);
        } else
                printf("using default port %i\n", port);

        initscr();
        //noecho();
        cbreak();
        nodelay(stdscr, TRUE);
        keypad(stdscr, TRUE);
        printw("Press F9 to quit\n");
        refresh();

        if ((he = gethostbyname(argv[1])) == NULL) {
                herror("gethostbyname");
                exit(1);
        }

        // set flags
        int flags = fcntl(sockfd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(sockfd, F_SETFL, flags);

        if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
                perror("socket");
                exit(1);
        }

        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons(port);
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        memset(&(their_addr.sin_zero), '\0', 8);

        if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
                perror("socket");
                exit(1);
        }

        while (keep_running) {
                while (true) {
                        if ((c = getch()) != ERR)
                                break;
                        usleep(100);
                        errno = 0;
                        if (recv(sockfd, rbuf, sizeof(rbuf), MSG_WAITALL) != -1 && 
(errno == EAGAIN || errno == EWOULDBLOCK))
                                break;
                }
                if (c == ERR) {
                        printf("Message from you: %c (%i)\n", c, c);
                        if (c == 19)
                                keep_running = FALSE;
                }
                else {
                        printf("Message from server: %s\n", rbuf);
                }
        }

        printf("quitting time.. hit any key to continue\n");
        nodelay(stdscr, FALSE);
        getch();
        endwin();
        close(sockfd);
        return 0;
}
I could recieve data from the socket if I removed the fnctl(), MSG_WAITALL and the errno-checking.

Last edited by Ephracis; 07-06-2006 at 12:15 PM.
 
Old 07-07-2006, 03:37 PM   #10
mhcox
Member
 
Registered: Aug 2005
Location: Albuquerque, NM
Distribution: Fedora
Posts: 30

Rep: Reputation: 15
Quote:
Originally Posted by Ephracis
OK. I am having a bit of trouble with your code. I have never used fnctl before. Should I use it before or after calling socket()?

The current code I have just seem to ignore data on both the socket and stdin. It just hang.

Here is my current code for the client.c:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <fcntl.h>
 
#include <ncurses.h>
 
#define MAXDATASIZE 1024
#define STDIN 0
 
int main(int argc, char *argv[]) {
 
        int sockfd, nbytes, c, keep_running = TRUE;
        char sbuf[MAXDATASIZE];
        char rbuf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr;
        fd_set read_fds;
 
        if (argc < 2 || argc > 4) {
                fprintf(stderr, "usage: client <hostname> [port] [nickname]\n");
                exit(1);
        }
 
        int port = 3067;
        if (argc > 2) {
                port = atoi(argv[2]);
                printf("using port %i\n", port);
        } else
                printf("using default port %i\n", port);
 
        initscr();
        //noecho();
        cbreak();
        nodelay(stdscr, TRUE);
        keypad(stdscr, TRUE);
        printw("Press F9 to quit\n");
        refresh();
 
        if ((he = gethostbyname(argv[1])) == NULL) {
                herror("gethostbyname");
                exit(1);
        }
 
 
        if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
                perror("socket");
                exit(1);
        }
 
        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons(port);
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        memset(&(their_addr.sin_zero), '\0', 8);
 
        if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
                perror("socket");
                exit(1);
        }
 
        // set flags.  May need to move this before the connect, but after the socket call.
        int flags = fcntl(sockfd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(sockfd, F_SETFL, flags);
 
        while (keep_running) {
                while (true) {
                        if ((c = getch()) != ERR)
                                break;
                        usleep(100);
                        errno = 0;
                        if (recv(sockfd, rbuf, sizeof(rbuf), MSG_WAITALL) != -1)
                                break;
                }
                if (c == ERR) {
                        printf("Message from you: %c (%i)\n", c, c);
                        if (c == 19)
                                keep_running = FALSE;
                }
                else {
                        printf("Message from server: %s\n", rbuf);
                }
        }
 
        printf("quitting time.. hit any key to continue\n");
        nodelay(stdscr, FALSE);
        getch();
        endwin();
        close(sockfd);
        return 0;
}
I could recieve data from the socket if I removed the fnctl(), MSG_WAITALL and the errno-checking.
Try the above modifications.
 
Old 07-07-2006, 07:48 PM   #11
Ephracis
Senior Member
 
Registered: Sep 2004
Location: Sweden
Distribution: Ubuntu, Debian
Posts: 1,109

Original Poster
Rep: Reputation: 50
Quote:
Originally Posted by mhcox
Try the above modifications.
Thank you. I think I've got it working now.. Finally I can continue.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Read data from a socket allomeen Linux - Networking 1 05-08-2006 03:19 AM
cannot read data at server socket, though client socket sends it jacques83 Linux - Networking 0 11-15-2005 01:58 PM
C++ Socket: How to read a long maldini1010 Programming 3 02-25-2005 03:56 PM
poor java socket read performance bobwall Linux - Networking 0 01-21-2005 09:55 PM
read socket adoyee Programming 3 02-26-2004 06:06 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:15 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration