Как я могу захватить одиночные нажатия клавиш в консольном приложении Unix без блокировки? - PullRequest
4 голосов
/ 08 октября 2008

У меня очень простой TCP-сервер, написанный на C. Он работает бесконечно, ожидая подключения. В Windows я использую select для проверки активности в сокете, и если ее нет, у меня есть следующий код, позволяющий мне выйти, нажав «q» на клавиатуре:

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

Это не работает в Unix, поскольку kbhit не существует и getch работает по-другому. Я нашел пример кода , который использует tcsetattr для изменения настроек терминала и разрешения посимвольного ввода. После вызова функции инициализации я открываю / dev / stdin (с O_NONBLOCK) и читаю символ, но read( f, &c, 1 ) блокируется, пока символ не ударится.

Полагаю, я мог бы создать отдельный поток и заставить it ждать неопределенно долго, а затем сигнализировать первому потоку, если пользователь нажал 'q', но это кажется немного сложным. Конечно, есть более простой способ?

Ответы [ 3 ]

5 голосов
/ 08 октября 2008

Добавьте stdin в список дескрипторов выбора, и, если у него есть данные, вызовите read, чтобы прочитать один символ из него.

1 голос
/ 17 октября 2008

В Unix, будь то на системной консоли или в окне терминала X, клавиатурный ввод / вывод проходит через виртуальный терминал. Устройство / dev / tty в наши дни является обычным способом доступа к управляющему терминалу процесса. Все манипуляции с устройством, кроме открытия / закрытия / чтения / записи, обрабатываются системным вызовом ioctl (2) для этого конкретного устройства. Общая идея того, что вы хотите сделать, это

Открыть управляющий терминал (который может быть или не быть стандартным)

Измените режим работы на этом терминале, чтобы он возвращался без ожидания полной строки ввода (что является нормальным значением по умолчанию)

Продолжайте работу с остальной частью вашей программы, зная, что чтение с этого терминала (которое может быть стандартным) может возвращать частичные строки или даже нулевые символы, не будучи ошибкой или условием завершения.


Подробный ответ о том, как выполнить второй шаг, можно найти в FAQ по C-программированию . Они также указывают, что это вопрос ОС, а не вопрос языка. Они предоставляют девять возможностей, но три основных, имеющих отношение к этому вопросу:

  1. Используйте библиотеку curses (3)
  2. Старые системы в стиле BSD, используйте sgttyb для установки CBREAK или RAW
  3. Posix или старые системы в стиле System V, используйте TCGETAW / TCSETAW для установки c_cc [VMIN] в 1 и c_cc [VTIME] в 0.

Следование нескольким ссылкам в C FAQ может привести к этой странице фрагментов кода kbhit .

1 голос
/ 08 октября 2008

Скорее, добавьте "f" из вашего

read( f, &c, 1 )

, чтобы выбрать вызов. Когда f готов к чтению, символ был нажат, и read () не будет блокировать.

...