Как предотвратить SIGPIPE или предотвратить завершение работы сервера?

Стандартная программа сервера TCP С++, использующая pthreads, привязка, прослушивание и принятие. У меня есть сценарий, по которому сервер заканчивается (read: crashes), когда я убиваю подключенного клиента.

Причиной сбоя является то, что вызов write() в файле не работает, поэтому программа получает SIGPIPE. И я думаю, это заставляет сервер выйти.

Я подумал: "Конечно, необработанный сигнал означает" выход ", поэтому используйте signal():

signal(SIGPIPE, SIG_IGN);

потому что, взятый из man 2 write:

EPIPE fd подключается к трубе или гнезду, конец считывания которого закрыт. Когда это произойдет, процесс записи также получит сигнал SIGPIPE. (Таким образом, значение возврата записи видно только в том случае, если программа ловит, блокирует или игнорирует этот сигнал.)

Увы, нет. Ни в потоке сервера, ни в клиентских потоках это не помогает.

Итак, , как я могу предотвратить вызов write() от повышения этого сигнала или (чтобы быть прагматичным), как остановить сервер от выхода.


Моя диагностика:

  • начальный поток сервера, привязка, прослушивание, прием.
  • позвольте клиенту подключиться (например, через telnet)
  • отправить pkill telnet, чтобы свернуть клиент.

нежелательное поведение: выходы сервера, в gdb с

... in write () at ../sysdeps/unix/syscall-template.S:82
82      T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)

и backtrace:

#0  ... in write () at ../sysdeps/unix/syscall-template.S:82
#1  ... in ClientHandler::mesg(std::string) ()
#2  ... in ClientHandler::handle() ()
#3  ... in start_thread (arg=<value optimized out>) at pthread_create.c:300
#4  ... in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#5  ... in ?? ()

Ответ 1

Неужели вы не проигнорировали signal до того, как отбросили какие-либо потоки? Если вы ждали, что позже один из других потоков все равно сможет получить сигнал и выйти из вашего приложения.

Если это не так, вы всегда можете написать запись poll/select, прежде чем пытаться записать, чтобы убедиться, что сокет доступен для записи.

Ответ 2

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

Итак, если вы установили обработчик сигнала, например: signal (SIGPIPE, SIG_IGN), и он, похоже, не работает, попробуйте запустить код вне отладчика.

Или установите handle SIGPIPE nostop (в подсказке gdb), чтобы предотвратить останов gdb по сигналу.

Ответ 3

Когда вы игнорируете SIGPIPE, вы больше не получаете сигнал SIGPIPE, но write() получает ошибку EPIPE.