UNIX: разработка сетевых приложений
Создание kqueue и добавление фильтровМы вызываем14-16, чтобы получить дескрипторkqueue, устанавливаем тайм- аут равным нулю, чтобы сделать вызовkqueueнеблокируемым, и наконец, вызываемkeventс массивомkeventна месте соответствующего аргумента.keventБесконечный цикл с блокированием в keventМы входим в бесконечный цикл и блокируемся в17-18. Функции передается пустой список изменений, потому что все интересующие нас события уже зарегистрированы, и нулевой тайм-аут, что позволяет заблокироваться навечно.keventПеребор возвращаемых событий в циклеМы проверяем все возвращаемые события и обрабатываем их последовательно.19Сокет готов для чтенияЭта часть кода ничем не отличается от листинга 6.2.20-28Вход готов для чтенияКод практически аналогичен листингу 6.2 за тем отличием, что нам приходится обрабатывать конец файла, возвращаемый29-40. Для каналов и терминаловkqueueвозвращает событие готовности дескриптора к чтению, подобноkqueue, так что мы можем считать из этого дескриптора символ конца файла. Для файловselectвозвращает количество байтов, оставшихся в полеkqueueструктурыdataи предполагает, что приложение само определит, когда оно доберется до конца этих данных. Поэтому мы переписываем цикл таким образом, чтобы отправлять данные по сети, если они были считаны из дескриптора. Затем проверяется достижение конца файла: если мы считали нуль байтов или если мы считали все оставшиеся байты из дескриптора файла, значит, это и естьstruct kevent. Еще одно изменение состоит в том, что вместоEOFдля удаления дескриптора из набора файлов мы используем флагFD_CLRи вызываемEV_DELETEдля удаления события из фильтра в ядре.keventРекомендации
Новыми интерфейсами следует пользоваться аккуратно. Читайте свежую документацию, относящуюся к конкретному выпуску операционной системы. Интерфейсы часто меняются от одного выпуска к другому, причем таким образом, что заметно это далеко не сразу. Все это продолжается до тех пор, пока поставщики не проработают все детали функционирования новых интерфейсов.
В целом, лучше избегать написания непереносимых программ. Однако для оптимизации ресурсоемких приложений годятся все средства.
14.10. Резюме
Существует три способа установить ограничение времени для операции с сокетом:
■ Использовать функцию
и сигналalarm.SIGALRM■ Задать предел времени в функции
.select■ Использовать более новые параметры сокета
иSO_RCVTIMEO.SO_SNDTIMEOПервый способ легко использовать, но он включает обработку сигналов и, как показано в разделе 20.5, может привести к ситуации гонок. Использование функции
означает, что блокирование происходит в этой функции (с заданным в ней пределом времени) вместо блокирования в вызове функцииselect,readилиwrite. Другая альтернатива — использование новых параметров сокета — также проста в использовании, но предоставляется не всеми реализациями.connectФункции
иrecvmsgявляются наиболее общими из пяти групп предоставляемых функций ввода-вывода. Они объединяют целый ряд возможностей, свойственных других функциям ввода-вывода, позволяя задавать флагsendmsg(как функцииMSG_xxxиrecv), возвращать или задавать адрес протокола собеседника (как функцииsendиrecvfrom), использовать множество буферов (как функцииsendtoиreadv). Кроме того, они обеспечивают две новых возможности: возвращение флагов приложению и получение или отправку вспомогательных данных.writevВ тексте книги мы описываем десять различных форм вспомогательных данных, шесть из которых появились в IPv6. Вспомогательные данные состоят из объектов вспомогательных данных. Перед каждым объектом идет структура
, задающая его длину, уровень протокола и тип данных. Пять макросов, начинающихся с префиксаcmsghdr, используются для создания и анализа вспомогательных данных.CMSG_Сокеты могут использоваться со стандартной библиотекой ввода-вывода С, но это добавляет еще один уровень буферизации к уже имеющемуся в TCP. На самом деле недостаток понимания буферизации, выполняемой стандартной библиотекой ввода-вывода, является наиболее общей проблемой при работе с этой библиотекой. Поскольку сокет не является терминальным устройством, общим решением этой потенциальной проблемы будет отключение буферизации стандартного потока ввода-вывода.
Многие производители предоставляют усовершенствованные средства опроса событий без накладных расходов на
иselect. Не стоит увлекаться написанием непереносимого кода, однако иногда преимущества перевешивают риск непереносимости.pollУпражнения
1. Что происходит в листинге 14.1, когда мы переустанавливаем обработчик сигналов, если процесс не установил обработчик для сигнала
?SIGALRM2. В листинге 14.1 мы выводим предупреждение, если у процесса уже установлен таймер
. Измените эту функцию так, чтобы новое значениеalarmдля процесса задавалось после выполнения connect до завершения функции.alarm3. Измените листинг 11.5 следующим образом: перед вызовом функции
вызовите функциюreadс флагомrecv. Когда она завершится, вызовите функциюMSG_PEEKс командойioctlи выведите число байтов, установленных в очередь в буфере приема сокета. Затем вызовите функциюFIONREADдля фактического чтения данных.read4. Что происходит с оставшимися в стандартном буфере ввода-вывода данными, если процесс, дойдя до конца функции
, не обнаруживает там функцииmain?exit5. Примените каждое из двух изменений, описанных после листинга 14.6, и убедитесь в том, что каждое из них решает проблему буферизации.