UNIX: разработка сетевых приложений
Та же проблема связана с вызовом
в листинге 6.1. Теперь данные скрываются от функцииreadlineуже не в буфереselect, а в буфереstdio. Вспомните, что в разделе 3.9 мы создали функцию, проверявшую состояние буфераreadline. Мы могли бы воспользоваться ею перед вызовомreadline, чтобы проверить, нет ли в буфереselectданных, дожидающихся обработки. Наша программа усложнится еще больше, если мы допустим, что буферreadlineможет содержать лишь часть строки (то есть нам придется дожидаться считывания этой строки целиком).readlineПроблемы буферизации мы постараемся решить в усовершенствованной версии
в разделе 6.7.str_cli6.6. Функция shutdown
Обычный способ завершить сетевое соединение — вызвать функцию
. Но у функцииcloseесть два ограничения, которых лишена функцияclose:shutdown1. Функция close последовательно уменьшает счетчик ссылок дескриптора и закрывает сокет, только если счетчик доходит до нуля. Мы рассматривали это в разделе 4.8. Используя функцию
, мы можем инициировать обычную последовательность завершения соединения TCP (четыре сегмента, начинающихся с FIN, на рис. 2.5) независимо от значения счетчика ссылок.shutdown2. Функция
завершает оба направления передачи данных — и чтение, и запись. Поскольку соединение TCP является двусторонним, возможны ситуации, когда нам понадобится сообщить другому концу соединения, что мы закончили отправку, даже если на том конце соединения имеются данные для отправки нам. Это случай, рассмотренный в предыдущем разделе при описании работы нашей функцииcloseв пакетном режиме. На рис. 6.10 показаны типичные вызовы функций в этом сценарии.str_cliРис. 6.10. Вызов функции shutdown для закрытия половины соединения TCP
#include <sys/socket.h>int shutdown(int <i>sockfd</i>, int <i>howto</i>);<i>Возвращает: 0 в случае успешного выполнения, -1 в случае ошибки</i>Действие функции зависит от значения аргумента
.howto■
. Закрывается считывающая половина соединения: из сокета больше нельзя считывать данные, и все данные, находящиеся в данный момент в буфере приема сокета, сбрасываются. Процесс больше не может выполнять функции чтения из сокета. Любые данные для сокета TCP, полученные после вызова функцииSHUT_RDс этим аргументом, подтверждаются и «молча» игнорируются.shutdownПРИМЕЧАНИЕПо умолчанию все, что записывается в маршрутизирующий сокет (см. главу 17), возвращается как возможный ввод на все маршрутизирующие сокеты узла. Некоторые программы вызывают функцию shutdown со вторым аргументом SHUT_RD, чтобы предотвратить получение подобной копии. Другой способ избежать копирования — отключить параметр сокета SO_USELOOPBACK.
■
. Закрывается записывающая половина соединения. В случае TCP это называется половинным закрытием (см. раздел 18.5 [111]). Все данные, находящиеся в данный момент в буфере отправки сокета, будут отправлены, а затем будет выполнена обычная последовательность действий по завершению соединения TCP. Как мы отмечали ранее, закрытие записывающей половины соединения выполняется независимо от того, является ли значение в счетчике ссылок дескриптора сокета положительным или нет. Процесс теряет возможность записывать данные в сокет.SHUT_WR■
. Закрываются и читающая, и записывающая половины соединения. Это эквивалентно двум вызовам функции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. Первая уведомляет нас о том, когда сервер закрывает свой конец соединения, а вторая позволяет корректно обрабатывать пакетный ввод. Эта версия избавлена от ориентации на строки. Вместо этого она работает с буферами, что позволяет полностью избавиться от проблем, описанных в конце раздела 6.5.shutdownЛистинг 6.2. функция str_cli, использующая функцию select, которая корректно обрабатывает конец файла
//select/strcliselect02.c1 #include "unp.h"2 void3 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(&rset);11 for (;;) {12 if (stdineof == 0)13 FD_SET(fileno(fp), &rset);14 FD_SET(sockfd, &rset);15 maxfdp1 = max(fileno(fp), sockfd) + 1;16 Select(maxfdp1, &rset, NULL, NULL, NULL);17 if (FD_ISSET(sockfd, &rset)) { /* сокет готов для чтения */18 if ((n = Read(sockfd, buf, MAXLINE)) == 0) {19 if (stdineof == 1)20 return; /* нормальное завершение */21 else22 err_quit("str_cli: server terminated prematurely");
