Книга: UNIX: разработка сетевых приложений
5.13. Сигнал SIGPIPE
5.13. Сигнал SIGPIPE
Что происходит, если клиент игнорирует возвращение ошибки из функции readline
и отсылает следующие данные серверу? Это может произойти, если, например, клиенту нужно выполнить две операции по отправке данных серверу перед считыванием данных от него, причем первая операция отправки данных вызывает RST.
Применяется следующее правило: когда процесс производит запись в сокет, получивший сегмент RST, процессу посылается сигнал SIGPIPE
. По умолчанию действием этого сигнала является завершение процесса, так что процесс должен перехватить сигнал, чтобы не произошло непроизвольного завершения.
Если процесс либо перехватывает сигнал и возвращается из обработчика сигнала, либо игнорирует сигнал, то операция записи возвращает ошибку EPIPE
.
ПРИМЕЧАНИЕ
Часто задаваемым вопросом (FAQ) в Usenet является такой: как получить этот сигнал при первой, а не при второй операции записи? Это невозможно. Как следует из приведенных выше рассуждений, первая операция записи выявляет сегмент RST, а вторая — сигнал. Если запись в сокет, получивший сегмент FIN, допускается, то запись в сокет, получивший сегмент RST, является ошибочной.
Чтобы увидеть, что происходит с сигналом SIGPIPE
, изменим код нашего клиента так, как показано в листинге 5.10.
Листинг 5.10. Функция str_cli, дважды вызывающая функцию writen
//tcpcliserv/str_cli11.c
Все изменения, которые мы внесли, — это повторный вызов функции
1 #include "unp.h"
2 void
3 str_cli(FILE *fp, int sockfd)
4 {
5 char sendline[MAXLINE], recvline[MAXLINE];
6 while (Fgets(sendline, MAXLINE, fp) != NULL) {
7 Writen(sockfd, sendline, 1);
8 sleep(1);
9 Writen(sockfd, sendline + 1, strlen(sendline) - 1);
10 if (Readline(sockfd, recvline, MAXLINE) == 0)
11 err_quit("str_cli, server terminated prematurely");
12 Fputs(recvline, stdout);
13 }
14 }
7-9writen
: сначала в сокет записывается первый байт данных, за этим следует пауза в 1 с и далее идет запись остатка строки. Наша цель — выявить сегмент RST при первом вызове функции writen
и генерировать сигнал SIGPIPE
при втором вызове.
Если мы запустим клиент на нашем узле Linux, мы получим:
linux % tcpcli11 127.0.0.1
hi there мы вводим эту строку
hi there и она отражается сервером
здесь мы завершаем дочерний процесс сервера
bye затем мы вводим эту строку
Broken pipe это сообщение выводится интерпретатором
Мы запускаем клиент, вводим одну строку, видим, что строка отражена корректно, и затем завершаем дочерний процесс сервера на узле сервера. Затем мы вводим другую строку (bye
), но ничего не отражается, а интерпретатор сообщает нам о том, что процесс получил сигнал SIGPIPE. Некоторые интерпретаторы не выводят никаких сообщений, если процесс завершает работу без дампа памяти, но в нашем примере использовался интерпретатор bash
, который берет на себя эту работу.
Рекомендуемый способ обработки сигнала SIGPIPE
зависит от того, что приложение собирается делать, когда получает этот сигнал. Если ничего особенного делать не нужно, проще всего установить действие SIG_IGN
, предполагая, что последующие операции вывода перехватят ошибку EPIPE
и завершатся. Если при появлении сигнала необходимо проделать специальные действия (возможно, запись в системный журнал), то сигнал следует перехватить и выполнить требуемые действия в обработчике сигнала. Однако отдавайте себе отчет в том, что если используется множество сокетов, то при доставке сигнала мы не получаем информации о том, на каком сокете произошла ошибка. Если нам нужно знать, какая именно операция write
вызвала ошибку, следует либо игнорировать сигнал, либо вернуть управление из обработчика сигнала и обработать ошибку EPIPE
из функции write
.
- 5.1. Введение
- 5.2. Эхо-сервер TCP: функция main
- 5.3. Эхо-сервер TCP: функция str_echo
- 5.4. Эхо-клиент TCP: функция main
- 5.5. Эхо-клиент TCP: функция str_cli
- 5.6. Нормальный запуск
- 5.7. Нормальное завершение
- 5.8. Обработка сигналов POSIX
- 5.9. Обработка сигнала SIGCHLD
- 5.10. Функции wait и waitpid
- 5.11. Прерывание соединения перед завершением функции accept
- 5.12. Завершение процесса сервера
- 5.13. Сигнал SIGPIPE
- 5.14. Сбой на узле сервера
- 5.15. Сбой и перезагрузка на узле сервера
- 5.16. Выключение узла сервера
- 5.17. Итоговый пример TCP
- 5.18. Формат данных
- 5.19. Резюме
- Упражнения
- 7.4. Аналоговые перемножители сигналов
- 15.1.3. Обработка сигналов управления заданиями
- Звуковые сигналы BIOS
- 12.6. Сигналы реального времени
- Пример: обработчик управляющих сигналов консоли
- 10.6.4. Перехват сигналов: sigaction()
- 7.2.6.3. Системные демоны и традиционные сигналы
- Сигналы и шумы
- 19.7.9. Обработка сигналов и протоколирование
- 10.6.3. Управление маской сигналов: sigprocmask() и др.
- 4.23.3. Запуск измерительной последовательности от внешнего сигнала
- Световые сигналы