ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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.
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! ) )
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.
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.
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.
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.
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.
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.
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.
}
}
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.