Литмир - Электронная Библиотека
Содержание  
A
A

Та же проблема связана с вызовом

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 показаны типичные вызовы функций в этом сценарии.

UNIX: разработка сетевых приложений - img_54.png

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

#include <sys/socket.h>

int shutdown(int <i>sockfd</i>, int <i>howto</i>);

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

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

howto
.

■ 

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

ПРИМЕЧАНИЕ

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

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

■ 

SHUT_RDWR
. Закрываются и читающая, и записывающая половины соединения. Это эквивалентно двум вызовам функции
shutdown
: сначала с аргументом
SHUT_RD
, затем — с аргументом
SHUT_WR
.

В табл. 7.4 приведены все возможные сценарии, доступные процессу при вызове функций

shutdown
и
close
. Действие функции close зависит от значения параметра сокета
SO_LINGER
.

ПРИМЕЧАНИЕ

Три константы SHUT_xxx определяются в спецификации POSIX. Типичные значения аргумента howto, с которыми вы встретитесь, — это 0 (закрытие читающей половины), 1 (закрытие записывающей половины) и 2 (закрытие обеих половин).

6.7. Функция str_cli (еще раз)

В листинге 6.2 представлена наша обновленная (и корректная) функция

str_cli
. В этой версии используются функции
select
и
shutdown
. Первая уведомляет нас о том, когда сервер закрывает свой конец соединения, а вторая позволяет корректно обрабатывать пакетный ввод. Эта версия избавлена от ориентации на строки. Вместо этого она работает с буферами, что позволяет полностью избавиться от проблем, описанных в конце раздела 6.5.

Листинг 6.2. функция str_cli, использующая функцию select, которая корректно обрабатывает конец файла

//select/strcliselect02.c

 1 #include &quot;unp.h&quot;

 2 void

 3 str_cli(FILE *fp, int sockfd)

 4 {

 5  int maxfdp1, stdineof;

 6  fd_set rset;

 7  char buf[MAXLINE];

 8  int n;

 9  stdineof = 0;

10  FD_ZERO(&amp;rset);

11  for (;;) {

12   if (stdineof == 0)

13    FD_SET(fileno(fp), &amp;rset);

14   FD_SET(sockfd, &amp;rset);

15   maxfdp1 = max(fileno(fp), sockfd) + 1;

16   Select(maxfdp1, &amp;rset, NULL, NULL, NULL);

17   if (FD_ISSET(sockfd, &amp;rset)) { /* сокет готов для чтения */

18    if ((n = Read(sockfd, buf, MAXLINE)) == 0) {

19     if (stdineof == 1)

20      return; /* нормальное завершение */

21     else

22      err_quit(&quot;str_cli: server terminated prematurely&quot;);

68
{"b":"225366","o":1}