int shutdown(int
Возвращает 0 при успешном завершении или -1 при ошибке
Данный системный вызов закрывает одно или оба направления сокета sockfd в зависимости от значения аргумента how, который может иметь одно из следующих значений.
• SHUT_RD — запрещает чтение из сокета. Последующие операции read() возвращают конец файла (0). При этом запись данных может продолжаться. После применения флага SHUT_RD к потоковому сокету домена UNIX приложение на другом конце соединения, пытающееся передать новые данные, получит сигнал SIGPIPE и ошибку EPIPE. Как отмечается в подразделе 57.6.6, использование данного флага для TCP-сокетов не имеет смысла.
• SHUT_WR — запрещает запись в сокет. Когда удаленное приложение прочитает все оставшиеся данные, оно обнаружит конец файла. Последующая запись в локальный сокет приведет к сигналу SIGPIPE и ошибке EPIPE. Данные, записываемые на другом конце соединения, по-прежнему можно будет прочитать. Иными словами, эта операция позволяет сообщить удаленному приложению о завершении файла, но притом иметь возможность продолжать чтение данных, которое посылает приложение. Флаг SHUT_WR используется в таких программах, как ssh и rsh (см. раздел 18.5 книги [Stevens, 1994]). Это наиболее распространенный способ применения вызова shutdown(), а соответствующий сокет иногда называют
• SHUT_RDWR — полностью закрывает соединение: как для чтения, так и для записи. То же, что последовательное выполнение операций SHUT_RD и SHUT_WR.
Помимо семантики аргумента how вызов shutdown() имеет еще одно существенное отличие от close(): он закрывает канал(-ы) сокета вне зависимости от того, ссылаются ли на этот сокет какие-нибудь другие файловые дескрипторы. (Иными словами, shutdown() оперирует описанием открытого файла, а не его дескриптором; см. рис. 5.1.) Представьте, к примеру, что sockfd указывает на подключенный потоковый сокет. Если сделать следующие вызовы, то соединение останется открытым и по-прежнему можно будет выполнять для него ввод/вывод, используя файловый дескриптор fd2:
fd2 = dup(sockfd);
close(sockfd);
Но если выполнить следующую последовательность вызовов, то оба канала соединения будут закрыты, а дескриптор fd2 станет непригодным для ввода/вывода:
fd2 = dup(sockfd);
shutdown(sockfd, SHUT_RDWR);
Нечто похожее происходит, когда во время вызова fork() файловый дескриптор сокета дублируется. Если после этого один из процессов выполнит для своей копии дескриптора операцию SHUT_RDWR, то другой процесс больше не сможет пользоваться своей копией для ввода/вывода.
Обратите внимание: shutdown() не закрывает файловый дескриптор, даже если аргумент how равен SHUT_RDWR. Чтобы добиться закрытия, нужно дополнительно вызвать close().
Использование вызова shutdown() с флагом SHUT_WR демонстрируется в листинге 57.2. Эта программа представляет собой TCP-клиент для службы echo (TCP-сервер для данной службы был показан в разделе 56.3). Чтобы сделать реализацию более компактной, мы задействуем функции из библиотеки для работы с сокетами интернет-домена из раздела 55.12.
В некоторых дистрибутивах Linux служба echo не включена по умолчанию. Следовательно, прежде чем запускать программу из листинга 57.2, ее следует включить. Обычно эта служба реализована внутри демона inetd(8) (см. раздел 56.5), и для ее активации нужно удалить комментарии из двух строчек в файле /etc/inetd.conf, относящихся к протоколам UDP и TCP (см. листинг 56.5), а затем послать сигнал SIGHUP демону inetd.
Многие дистрибутивы предоставляют вместо inetd(8) более современный демон xinetd(8). Чтобы узнать, как выполнить для него аналогичные изменения, обратитесь к его документации.
В качестве единственного аргумента командной строки программа принимает имя сетевого узла, на котором запущена служба echo. Клиент выполняет вызов fork(), разделяясь на родительский и дочерний процессы.
Родитель записывает содержимое стандартного ввода в сокет, чтобы сервер echo мог его прочитать. При обнаружении конца файла в этом вводе родитель использует вызов shutdown() для закрытия записывающего канала своего сокета. В результате сервер echo тоже сталкивается с завершением файла и закрывает свой сокет.
Дочерний процесс клиента считывает ответ сервера echo из сокета и направляет его в стандартный вывод. Обнаружив конец файла, он завершает работу.
Ниже показан пример вывода, который можно увидеть при выполнении этой программы:
$ cat > tell-tale-heart.txt
It is impossible to say how the idea entered my brain;
but once conceived, it haunted me day and night.
$ ./is_echo_cl tekapo < tell-tale-heart.txt
It is impossible to say how the idea entered my brain;
but once conceived, it haunted me day and night.
Листинг 57.2. Клиент службы echo