Вход/Регистрация
UNIX: разработка сетевых приложений
вернуться

Стивенс Уильям Ричард

Шрифт:

Чтобы понять происходящее, обратите внимание, что в пакетном режиме мы отправляем запросы так быстро, как их может принять сеть. Сервер обрабатывает их и отправляет обратно ответы с той же скоростью. Это приводит к тому, что в момент времени 7 канал целиком заполнен, как показано на рис. 6.9.

Рис. 6.9. Заполнение канала между клиентом и сервером: пакетный режим

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

ПРИМЕЧАНИЕ

Существуют различные нюансы, имеющие отношение к передаче большого количества данных TCP (bulk data flow), которые мы здесь игнорируем. К ним относятся алгоритм медленного запуска (slow start algorithm), ограничивающий скорость, с которой данные отправляются на новое или незанятое соединение, и возвращаемые сегменты ACK. Все эти вопросы рассматриваются в главе 20 [111].

Чтобы увидеть, в чем заключается проблема с нашей функцией

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.

6.6. Функция shutdown

Обычный способ завершить сетевое соединение — вызвать функцию

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

1. Функция close последовательно уменьшает счетчик ссылок дескриптора и закрывает сокет, только если счетчик доходит до нуля. Мы рассматривали это в разделе 4.8. Используя функцию

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

2. Функция

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

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

#include <sys/socket.h>

int shutdown(int sockfd, int howto);

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

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

howto
.

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

ПРИМЕЧАНИЕ

По умолчанию все, что записывается в маршрутизирующий сокет (см. главу 17), возвращается как возможный ввод на все маршрутизирующие сокеты узла. Некоторые программы вызывают функцию shutdown со вторым аргументом SHUT_RD, чтобы предотвратить получение подобной копии. Другой способ избежать копирования — отключить параметр сокета SO_USELOOPBACK.

SHUT_WR
. Закрывается записывающая половина соединения. В случае TCP это называется половинным закрытием(см. раздел 18.5 [111]). Все данные, находящиеся в данный момент в буфере отправки сокета, будут отправлены, а затем будет выполнена обычная последовательность действий по завершению соединения TCP. Как мы отмечали ранее, закрытие записывающей половины соединения выполняется независимо от того, является ли значение в счетчике ссылок дескриптора сокета положительным или нет. Процесс теряет возможность записывать данные в сокет.

  • Читать дальше
  • 1
  • ...
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: