首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何处理生成SIGPIPE对象内的SIGPIPE错误?

如何处理生成SIGPIPE对象内的SIGPIPE错误?
EN

Stack Overflow用户
提问于 2014-07-23 20:53:47
回答 2查看 4.1K关注 0票数 6

我有两个应用程序,一个是服务器,另一个是客户端,都是用C++和Qt编写的,但它们都使用一个C库,它使用C套接字方法在它们之间执行套接字通信(这都是在Linux中实现的)。

当两者连接并且我关闭客户端时,当服务器试图向它发送一条新消息时,它将得到一个SIGPIPE错误并关闭。我在网络上做了一些研究,为了了解如何为SIGPIPE创建一个处理程序,所以我不关闭应用程序,而是告诉定时器不断发送信息停止。

现在,我确实学会了如何简单地处理信号:创建一个在main()或全局(注意:从SO和yes了解到,我知道信号()是过时的)中接收int并使用信号(SIGPIPE,myMethod)的方法。

但问题是,通过这样做,我无法停止向死客户端发送信息,因为处理信号的方法要么位于发送消息的类之外,要么位于静态方法,后者无法访问我的服务器对象。

为了澄清,下面是当前的体系结构:

//main.cpp

代码语言:javascript
复制
void signal_callback_handler(int signum)
{
    qDebug() << "Caught signal SIGPIPE" << signum << "; closing the application";

    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setApplicationName("ConnEmulator");
    app.setApplicationVersion("1.0.0");
    app.setOrganizationName("Embrasul");
    app.setOrganizationDomain("http://www.embrasul.com.br");

    MainWidget window;
    window.show();

    /* Catch Signal Handler SIGPIPE */
    signal(SIGPIPE, signal_callback_handler);

    return app.exec();
}

// MainWidget类(简化)

代码语言:javascript
复制
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWidget),
    timerSendData(new QTimer(this))
{
    ui->setupUi(this);

    connect(timerSendData,SIGNAL(timeout()),this,SLOT(slotSendData()));

    timerSendData->start();
    //...
}

void MainWidget::slotSendData()
{
    //Prepares data
    //...

    //Here the sending message is called with send()
    if (hal_socket_write_to_client(&socket_descriptor, (u_int8_t *)buff_write, myBufferSize) == -1)
        qDebug() << "Error writting to client";
}

//套接字库

代码语言:javascript
复制
int hal_socket_write_to_client(socket_t *obj, u_int8_t *buffer, int size)
{
    struct s_socket_private * const socket_obj = (struct s_socket_private *)obj;
    int retval = send(socket_obj->client_fd, buffer, size, 0);

    if (retval < 0)
        perror("write_to_client");

    return retval;
}

那么,如何使在MainWidget中创建的int main()对象处理该信号,以便他可以调用timerSendData->stop()

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-23 21:08:52

SIGPIPE是丑陋的,但是可以用一种完全封装、线程安全的方式来处理,除了编写可能导致SIGPIPE的代码之外,什么都不影响。一般的方法是:

  1. SIGPIPE阻止pthread_sigmask (或sigprocmask,但后者不能保证在多线程程序中是安全的)并保存原始信号掩码。
  2. 执行可能引发SIGPIPE的操作。
  3. 使用零超时调用sigtimedwait,以使用任何挂起的SIGPIPE信号。
  4. 恢复原始信号掩码(如果以前解除阻塞,则解锁SIGPIPE )。

下面是使用此方法的一些示例代码的尝试,其形式是write的纯包装器,以避免SIGPIPE

代码语言:javascript
复制
ssize_t write_nosigpipe(int fd, void *buf, size_t len)
{
    sigset_t oldset, newset;
    ssize_t result;
    siginfo_t si;
    struct timespec ts = {0};

    sigemptyset(&newset);
    sigaddset(&newset, SIGPIPE);
    pthread_sigmask(SIG_BLOCK, &newset, &oldset);

    result = write(fd, buf, len);

    while (sigtimedwait(newset, &si, &ts)>=0 || errno != EAGAIN);
    pthread_sigmask(SIG_SETMASK, &oldset, 0);

    return result;
}

它还没有经过测试(甚至还没有编译),可能需要一些小的修复,但希望能够理解这一点。显然,为了提高效率,您可能希望在比单个write调用更大的粒度上执行此操作(例如,您可以在整个库函数的持续时间内阻止SIGPIPE,直到它返回到外部调用方为止)。

另一种设计是简单地阻塞SIGPIPE,永远不要解除阻塞,并在函数的接口中记录它使SIGPIPE阻塞的情况(注意:阻塞是线程本地的,不会影响其他线程),并且可能会使SIGPIPE挂起(处于阻塞状态)。如果需要的话,调用方将负责恢复它,因此想要SIGPIPE的罕见调用者可以通过解锁信号来获得它(但在您的函数完成之后),而大多数调用者可以很高兴地让它被阻塞。阻塞代码的工作方式类似于上面的代码,删除了sigtimedwait/unblocking部件。这与Maxim的回答类似,只是影响是线程本地的,因此线程是安全的。

票数 7
EN

Stack Overflow用户

发布于 2014-07-23 21:00:13

现在我学习了如何简单地处理信号:创建一个接收int并使用信号的方法(SIGPIPE,myMethod)

您只需要忽略SIGPIPE,就不需要处理程序:

代码语言:javascript
复制
// don't raise SIGPIPE when sending into broken TCP connections
::signal(SIGPIPE, SIG_IGN); 

但问题是,通过这样做,我无法停止向死客户端发送信息,因为处理信号的方法要么位于发送消息的类之外,要么位于静态方法,后者无法访问我的服务器对象。

当忽略将SIGPIPE写入损坏的TCP连接时,会返回错误代码EPIPE,您使用的套接字包装器应该处理这个错误代码,就像连接已经关闭一样。理想情况下,套接字包装器应该将MSG_NOSIGNAL标志传递给send,这样send就不会引发SIGPIPE

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24920748

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档