views:

856

answers:

3

I have a very simple TCP server written in C. It runs indefinitely, waiting for connections. On Windows, I use select to check for activity on the socket, and if there isn't any, I have the following code to allow me to quit by hitting 'q' on the keyboard:

if( kbhit() ) {
   char c = getch();
   if( c == 'q' ) break;
}

This doesn't work on unix, since kbhit doesn't exist and getch works differently. I found some sample code that uses tcsetattr to change the terminal settings and allow character-by-character input. After calling the init function, I open /dev/stdin (with O_NONBLOCK) and read a character, but read( f, &c, 1 ) blocks until a character is hit.

I suppose I could spawn a separate thread and have it wait indefinitely and then signal the first thread if the user hits 'q', but that seems a little heavy-handed. Surely there's an easier way?

+5  A: 

Add stdin to your list of select handles, and if it has data, call read to read one character from it.

Lou Franco
Even if my app isn't already using select, this will work. The fact that I am using select already makes it very easy, and it works like a charm. Thanks
Graeme Perrow
+1  A: 

Rather, add "f" from your

read( f, &c, 1 )

to select call. When f is ready for read, a character has been pressed, and read() will not block.

Arkadiy
+1  A: 

In Unix, whether on the system console or in an X terminal window, keyboard I/O goes through a virtual terminal. Device /dev/tty is the usual way, these days, of accessing a process's controlling terminal. Device manipulations other than open/close/read/write are all handled by the ioctl(2) system call for that specific device. The general idea of what you want to do is

Open the controlling terminal (which may or may not be stdin)

Change the operating mode on that terminal to return without waiting for a full line of input (which is the normal default)

Continue with the rest of your program, knowing that reads from that terminal (which might be stdin) may return partial lines or even zero characters without it being an error or termination condition.


A detailed answer on how to do the second step is found at the C-programming faq. They also point out that this is an OS question, not a language question. They provide nine possibilities, but the three major ones relevant to this question are

  1. Use the curses(3) library
  2. Old BSD style systems, use sgttyb to set CBREAK or RAW
  3. Posix or old System V style systems, use TCGETAW/TCSETAW to set c_cc[VMIN] to 1 and c_cc[VTIME] to 0.

Following a couple of references in the C FAQ could lead to to this page of kbhit code fragments.

mpez0