Как вырваться из fgets?

Я пишу простой клиент и сервер для обмена мгновенными сообщениями, чтобы разобраться с программированием сокетов.

У моего клиента есть два потока:

  • Поток A имеет соединение с сервером через потоковое сокет и выполняет чтение в цикле, печатая строки текста, которые он получает от сервера. Если строка чтения возвращает EOF, цикл завершается.
  • Поток B прослушивает ввод с клавиатуры от пользователя, используя fgets в цикле. Когда пользователь нажимает ввод, он отправляет строку на сервер (чтобы он мог переслать ее другому клиенту).

Когда пользователь нажимает Ctrl-D, клиент отправляет на сервер специальное сообщение о том, что «клиент хочет отключиться», после чего сервер закрывает дескриптор файла подключения для этого пользователя. Это заставляет поток A выйти из цикла, поскольку функция readline возвращает EOF. Затем поток A закрывает дескриптор файла подключения и завершает работу.

Тем временем поток B все еще слушает ввод пользователя с клавиатуры, пока он не нажмет Enter. В идеале fgets прерывались бы раньше и давали бы пользователю знать, что другой клиент отключился.

Есть ли что-то еще, или мне нужно использовать другую функцию ввода или библиотеку?


person master_latch    schedule 14.01.2014    source источник
comment
На какой ОС вы кодируете? Как создавались эти темы? (т.е. они pthreads?)   -  person Mike    schedule 15.01.2014
comment
cygwin для Windows, но он должен быть совместим с *nix. Они птреды, да.   -  person master_latch    schedule 15.01.2014


Ответы (3)


Во-первых, если вы пытаетесь написать функции сокета, не используйте fgets() или что-либо еще, что использует буферизованный ввод-вывод, иначе известный как FILE *. Вместо этого используйте файловые дескрипторы (fd). Как правило, следует избегать каждой функции libc, начинающейся с 'f'. Вы хотите read и write.

Во-вторых, вы хотите прочитать об асинхронном вводе-выводе с помощью select(), а не решать, как «вырваться» из fgets().

В-третьих, я мог бы дать вам руководство здесь, или я мог бы порекомендовать вам использовать Google или посмотреть на http://en.wikipedia.org/wiki/Asynchronous_I/O, но на самом деле то, что вы хотите найти, — это копия Стивенса (по памяти «Расширенное программирование в среде Unix» — это то, что вам нужно, но на самом деле вы должны купить все из них и прикрепите их к своему телу, пока вы спите, в надежде научиться осмосу).

В-четвертых, я знаю, что вы сказали, что хотите сделать это с помощью потоков. Вы можете убить поток с помощью pthread_cancel(), если вы действительно хотите это сделать, и перезапустить его. Не. Делайте это правильно. Вам не нужны нити.

person abligh    schedule 14.01.2014
comment
fgets используется для получения ввода с клавиатуры, а не данных из дескриптора файла подключения к сокету. Я читал о pthread_cancel, но видел, как вы говорите, что это неодобрительно. - person master_latch; 15.01.2014
comment
fgets() используется для буферизации ввода из FILE *, не обязательно ввода с клавиатуры. Однако вам нужна дисциплина ввода-вывода, которая справится как с сокетами, так и с клавиатурой. Для этого вам нужно избегать буферизации библиотекой C. - person abligh; 15.01.2014

В среде Windows fgets является «блокирующим» вызовом. Таким образом, поток, который выдает его, будет ждать, пока не получит некоторый ввод.

Не проблема, если fgets входит в состояние "Alertable Wait", так что ожидающий ввод-вывод может быть отменен оператором ExitThread(0).

Опять же, в Windows способ выдачи инструкции ExitThread(0) в потоке, который находится в ожидании, заключается в том, чтобы запланировать APC (т. е. QueueUserAPC()) для потока и заставить этот запланированный метод выдать инструкцию ExitThread().

Я только что сделал это для некоторого кода, который я пишу. Я знаю, что APC вызовет выход потока, если этот поток выдал изменяемое ожидание. Я не знаю, делает ли fgets это в Windows, это то, что вам нужно выяснить. Если нет, то используйте оператор ввода-вывода, который делает. Примечание. В Windows ваш код может вызвать ожидание Alertable с помощью WaitSingleObjectEx() для дескриптора объекта, когда дескриптор получает сигнал, когда для объекта доступен ввод-вывод.

Выполните поиск в Интернете по «MSDN APC», и вы найдете всевозможную документацию от Microsoft по этому поводу.

person JackCColeman    schedule 14.01.2014

Потоки? Используйте pthread_kill для отправки SIGHUP. Это приведет к завершению работы fgets с errno, установленным в EINTR. Отправьте его из потока A до того, как он выйдет в поток B. Возможно, вам придется поиграть с обработчиками сигналов и масками с помощью pthread_sigmask и sigaction, в зависимости от того, насколько фантастично вы хотите получить.

person Chris J. Kiick    schedule 14.01.2014
comment
освободит ли это любые блокировки, удерживаемые потоком? Например, блокировка, которую он может иметь, потому что он находится внутри fgets? (потому что я только что узнал, что в Windows TerminateThread не освобождает критическую секцию, хранящуюся в fgets.) - person Spike0xff; 21.09.2016