Я пишу C ++ SSL-сервер для TCP-соединений в Linux.
Когда программа использует SSL_write()
для записи в закрытый канал генерируется исключение SIGPIPE-Exception, которое приводит к закрытию программы. Я знаю, что это нормальное поведение. Но программа не всегда должна умирать, когда узел не закрывает соединение правильно.
Я уже много гуглил и перепробовал почти все, что нашел, но, похоже, у меня ничего не работает. signal(SIGPIPE,SIG_IGN)
не работает — исключение по-прежнему выдается (то же самое для signal(SIGPIPE, SomeKindOfHandler)
,
Вывод GDB:
Program received signal SIGPIPE, Broken pipe.
0x00007ffff6b23ccd in write () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) where
#0 0x00007ffff6b23ccd in write () from /lib/x86_64-linux-gnu/libpthread.so.0
#1 0x00007ffff7883835 in ?? () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2 0x00007ffff7881687 in BIO_write () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#3 0x00007ffff7b9d3e0 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#4 0x00007ffff7b9db04 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#5 0x000000000042266a in NetInterface::SendToSubscribers(bool) () at ../Bether/NetInterface.h:181
#6 0x0000000000425834 in main () at ../Bether/main.cpp:111
О коде:
Я использую поток, который ждет новых подключений и принимает их. Затем поток помещает информацию о соединении (BIO & SSL) в статическую карту внутри NetInterface
учебный класс.
Каждые 5 секунд NetInterface::sendTOSubscribers()
выполнен из main()
, Эта функция обращается к статической карте и отправляет данные каждому подключенному там. Эта функция также является источником SIGPIPE.
я использовал signal(SIGPIPE,SIG_IGN)
в main()
(очевидно, перед 5-секундным циклом) и в NetInterface::SendToSubscribers()
, но это нигде не работает.
Спасибо за вашу помощь!
Вы должны вызвать функцию sigaction, чтобы изменить это поведение, либо игнорировать SIGPIPE, либо обрабатывать его особым образом с помощью собственного обработчика сигнала. Пожалуйста, не используйте функциональный сигнал, он устарел.
http://man7.org/linux/man-pages/man2/sigaction.2.html
Один из способов сделать это (я не скомпилировал этот код, но должен быть примерно таким):
void sigpipe_handler(int signal)
{
...
}
int main()
{
struct sigaction sh;
struct sigaction osh;
sh.sa_handler = &sigpipe_handler; //Can set to SIG_IGN
// Restart interrupted system calls
sh.sa_flags = SA_RESTART;
// Block every signal during the handler
sigemptyset(&sh.sa_mask);
if (sigaction(SIGPIPE, &sh, &osh) < 0)
{
return -1;
}
...
}
Если программа многопоточная, она немного отличается, так как у вас меньше контроля над тем, какой поток получит сигнал. Это зависит от типа сигнала. Для SIGPIPE он будет отправлен в pthread, который сгенерировал сигнал. Тем не менее, sigaction должен работать хорошо.
Можно установить маску в основном потоке, и все последующие созданные потоки будут наследовать маску сигнала. В противном случае маска сигнала может быть установлена в каждом потоке.
sigset_t blockedSignal;
sigemptyset(&blockedSignal);
sigaddset(&blockedSignal, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &blockedSignal, NULL);
Однако, если вы заблокируете сигнал, он будет в ожидании процесса и, как только это станет возможным, он будет доставлен. Для этого случая используйте sigtimedwait в конце потока. sigaction, установленный в главном потоке или в потоке, который генерировал SIGPIPE, также должен работать.
Я нашел решение, оно работает с pthread_sigmask.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
return -1;
Спасибо всем за помощь!