C++ 和 OpenSSL:在封闭管道中写入时的 SIGPIPE

C++ & OpenSSL: SIGPIPE when writing in closed pipe

我正在为 Linux 上的 TCP 连接编写 C++ SSL 服务器。 当程序使用 SSL_write() 写入关闭的管道时,会抛出 SIGPIPE 异常,导致程序关闭。我知道这是正常行为。但是当对等方没有正确关闭连接时,程序不应该总是死掉。

我已经在谷歌上搜索了很多,并尝试了几乎所有我找到的东西,但似乎没有任何东西适合我。 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 class 内的静态映射中。 每 5 秒 NetInterface::sendTOSubscribers()main() 开始执行。此函数访问静态映射并将数据发送到其中的每个连接。这个函数也是 SIGPIPE 的来源。 我在 main()(显然在 5 秒循环之前)和 NetInterface::SendToSubscribers() 中使用了 signal(SIGPIPE,SIG_IGN),但它在任何地方都不起作用。

感谢您的帮助!

您必须调用函数 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 应该可以正常工作。

可以在主线程中设置掩码,所有后续创建的pthreads都将继承信号掩码。否则,可以在每个线程中设置信号掩码。

sigset_t blockedSignal; 
sigemptyset(&blockedSignal);    
sigaddset(&blockedSignal, SIGPIPE); 
pthread_sigmask(SIG_BLOCK, &blockedSignal, NULL);

但是,如果您阻止信号,它将等待处理,并会尽快交付。对于这种情况,请在线程末尾使用 sigtimedwait。在主线程或生成 SIGPIPE 的线程中设置的 sigaction 也应该有效。

我找到了解决方案,它适用于 pthread_sigmask。

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
    return -1;

感谢大家的帮助!