Читаем UNIX: разработка сетевых приложений полностью

Чтобы увидеть, в чем заключается проблема с нашей функцией str_cli, представленной в листинге 6.1, будем считать, что файл ввода содержит только девять строк. Последняя строка отправляется в момент времени 8, как показано на рис. 6.9. Но мы не можем закрыть соединение после записи этого запроса, поскольку в канале еще есть другие запросы и ответы. Причина возникновения проблемы кроется в нашем способе обработки конца файла при вводе, когда процесс возвращается в функцию main, которая затем завершается. Но в пакетном режиме конец файла при вводе не означает, что мы закончили читать из сокета — в нем могут оставаться запросы к серверу или ответы от сервера.

Нам нужен способ закрыть одну половину соединения TCP. Другими словами, мы хотим отправить серверу сегмент FIN, тем самым сообщая ему, что закончили отправку данных, но оставляем дескриптор сокета открытым для чтения. Это делается с помощью функции shutdown, которая описывается в следующем разделе.

Вообще говоря, буферизация ввода-вывода для повышения производительности приводит к усложнению сетевых приложений (от чего пострадала и программа в листинге 6.1). Рассмотрим пример, в котором из стандартного потока ввода считывается несколько строк текста. Функция select передаст управление строке 20, в которой функция fgets считает доступные данные в буфер библиотеки stdio. Однако эта функция возвратит приложению только одну строку, а все остальные так и останутся в буфере. Считанная строка будет отправлена серверу, после чего будет снова вызвана функция select, которая будет ждать появления новых данных в стандартном потоке ввода несмотря на наличие еще не обработанных строк в буфере stdio. Причина в том, что select ничего не знает о буферах stdio и сообщает о доступности дескриптора для чтения с точки зрения системного вызова read, а не библиотечного вызова fgets. По этой причине использование fgets и select в одной программе считается опасным и требует особой осторожности.

Та же проблема связана с вызовом readline в листинге 6.1. Теперь данные скрываются от функции select уже не в буфере stdio, а в буфере readline. Вспомните, что в разделе 3.9 мы создали функцию, проверявшую состояние буфера readline. Мы могли бы воспользоваться ею перед вызовом select, чтобы проверить, нет ли в буфере readline данных, дожидающихся обработки. Наша программа усложнится еще больше, если мы допустим, что буфер readline может содержать лишь часть строки (то есть нам придется дожидаться считывания этой строки целиком).

Проблемы буферизации мы постараемся решить в усовершенствованной версии str_cli в разделе 6.7.

<p>6.6. Функция shutdown</p>

Обычный способ завершить сетевое соединение — вызвать функцию close. Но у функции close есть два ограничения, которых лишена функция shutdown:

1. Функция close последовательно уменьшает счетчик ссылок дескриптора и закрывает сокет, только если счетчик доходит до нуля. Мы рассматривали это в разделе 4.8. Используя функцию shutdown, мы можем инициировать обычную последовательность завершения соединения TCP (четыре сегмента, начинающихся с FIN, на рис. 2.5) независимо от значения счетчика ссылок.

2. Функция close завершает оба направления передачи данных — и чтение, и запись. Поскольку соединение TCP является двусторонним, возможны ситуации, когда нам понадобится сообщить другому концу соединения, что мы закончили отправку, даже если на том конце соединения имеются данные для отправки нам. Это случай, рассмотренный в предыдущем разделе при описании работы нашей функции str_cli в пакетном режиме. На рис. 6.10 показаны типичные вызовы функций в этом сценарии.

Рис. 6.10. Вызов функции shutdown для закрытия половины соединения TCP

#include

int shutdown(int sockfd, int howto);

Возвращает: 0 в случае успешного выполнения, -1 в случае ошибки

Действие функции зависит от значения аргумента howto.

■ SHUT_RD. Закрывается считывающая половина соединения: из сокета больше нельзя считывать данные, и все данные, находящиеся в данный момент в буфере приема сокета, сбрасываются. Процесс больше не может выполнять функции чтения из сокета. Любые данные для сокета TCP, полученные после вызова функции shutdown с этим аргументом, подтверждаются и «молча» игнорируются.

Перейти на страницу:

Все книги серии Мастер-класс

Секреты резьбы по дереву
Секреты резьбы по дереву

Изделия из древесины и материалов, имитирующих ее текстуру, привычным образом окружают нас в повседневной жизни, поэтому мы относимся к ней как к чему-то обыденному. Но как только ее коснется умелая рука мастера резьбы по дереву, рождается произведение искусства и раскрываются такие качества древесины, как богатая фактура, разнообразие цветов, особая теплота. Эта книга поможет читателю открыть для себя удивительный мир творчества и познать секреты резьбы по дереву. Автор надеется, что начинающие резчики найдут в ней интересный и полезный материал, который позволит им стать мастерами. В приложении представлены рисунки орнаментов и различных узоров, которые на первых порах можно копировать, а по мере приобретения навыка на их основе разрабатывать свои образцы.

Галина Алексеевна Серикова

Сделай сам / Хобби и ремесла / Руководства / Дом и досуг / Словари и Энциклопедии

Похожие книги