如何在生成SIGPIPE错误的对象中处理它
How to handle a SIGPIPE error inside the object that generated it?
我有两个应用程序,一个服务器和另一个客户端,都是用c++和Qt编写的,但它们都使用C库,该库使用C套接字方法在它们之间执行套接字通信(这都是在Linux中)。
当它们都连接并且我关闭客户端时,当服务器试图向它发送新消息时,它得到一个SIGPIPE错误并关闭。我在web和SO中做了一些研究,看看如何为SIGPIPE创建一个处理程序,而不是关闭应用程序,我将告诉不断发送信息的计时器停止。
现在我确实学会了如何简单地处理信号:创建一个接收int的方法,并在main()或global(注意:从SO中学到的,是的,我知道signal()已经过时了)中使用signal()。
但是问题是,通过这种方式,我无法停止向死亡客户端发送信息,因为处理信号的方法需要在发送消息的类或静态方法之外,这些方法不能访问我的服务器对象。
为了澄清,下面是当前的架构:
//main.cppvoid 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类(简化)
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";
}
//插座库
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()
?
SIGPIPE
是丑陋的,但是可以用一种完全封装的、线程安全的方式来处理,并且除了可能导致SIGPIPE
的写代码之外不会影响任何东西。一般方法为:
-
用
pthread_sigmask
(或sigprocmask
,但后者在多线程程序中不能保证安全)阻塞SIGPIPE
,并保存原始信号掩码。 -
执行可能引发
SIGPIPE
异常的操作。 -
以零超时调用
sigtimedwait
以消耗任何挂起的SIGPIPE
信号。 -
恢复原始信号掩码(如果
SIGPIPE
之前被解除阻塞,则解除阻塞)
下面是使用该方法的一些示例代码,以write
的纯包装形式避免了SIGPIPE
:
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的答案类似,只是影响是线程局部的,因此是线程安全的。
现在我确实学习了如何简单地处理信号:创建一个接收int的方法并使用signal(SIGPIPE, myMethod)
您只需要忽略SIGPIPE
,不需要处理程序:
// don't raise SIGPIPE when sending into broken TCP connections
::signal(SIGPIPE, SIG_IGN);
但是问题是,通过这种方式,我无法停止向死亡客户端发送信息,因为处理信号的方法需要在发送消息的类或静态方法之外,这些方法不能访问我的服务器对象。
当SIGPIPE
被忽略时,写入断开的TCP连接返回错误代码EPIPE
,您使用的套接字包装器应该像连接已关闭一样处理它。理想情况下,套接字包装器应该将MSG_NOSIGNAL
标志传递给send
,这样send
就不会引发SIGPIPE
。
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 如何使用对C函数和类对象的外部调用来处理C++头文件
- 如何处理从一个对象传递到另一个在C++中具有公共抽象类的对象的消息
- 使用对象数组对 SFML 进行动画处理
- CPU 瓶颈;处理具有许多非静态对象的 3D 场景渲染的简单方法
- 使用对象的基类部分模板专用化对对象进行哈希处理::哈希
- C++:处理线程本地对象销毁
- 传递多处理.将对象值为 ctype 函数?
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 使用返回对象的函数处理错误
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 处理从列表中删除指向对象的指针的"healthy"方法是什么?
- C++如何处理同名对象的向量?
- static是如何使用ClassA::m_variable处理所有类对象的
- 当线程处理不同的类时,应该在哪里声明条件变量、互斥对象
- 处理无法按预期方式创建的对象,c++
- 我可以将类作为C 中的对象处理
- QT良好的全局变量/对象处理
- 如何将map和unordered_map作为同一对象处理
- 强制一个对象处理输入的信号