I have two applications, one server and other client, both written in C++ and Qt, but both of them also uses a C library that uses C socket methods to perform a socket communication between them (and this all in Linux).
When both of them are connected and I close the client, when the server tries to send a new message to it, it gets a SIGPIPE error and closes. I did some research on the web and in SO to see how could I create a handler for the SIGPIPE so instead of closing the application, I'ld tell the timers that constantly send the information to stop.
Now I did learn how to simply handle the signal: create a method that receives a int and use signal(SIGPIPE, myMethod) inside main() or global (note: learned that from SO and yes, I know that signal() is obsolete).
But the problem is that by doing this way I'm unable to stop the sending of information to the dead client, for the method that handles the signal needs to be either outside the class which sends the message or a static method, which don't have access to my server object.
To clarify, here is the current architecture:
//main.cpp
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();
}
//The MainWidget class (simplified)
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";
}
//Socket library
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;
}
So how can I make my MainWidget object created inside int main()
handle the signal so he may call timerSendData->stop()
?
SIGPIPE
is ugly, but possible to deal with in a way that's fully encapsulated, thread-safe, and does not affect anything but the code making the write that might cause SIGPIPE
. The general method is:
Block SIGPIPE
with pthread_sigmask
(or sigprocmask
, but the latter is not guaranteed to be safe in multi-threaded programs) and save the original signal mask.
Perform the operation that might raise SIGPIPE
.
Call sigtimedwait
with a zero timeout to consume any pending SIGPIPE
signal.
Restore the original signal mask (unblocking SIGPIPE
if it was unblocked before).
Here's a try at some sample code using this method, in the form of a pure wrapper to write
that avoids 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;
}
It's untested (not even compiled) and may need minor fixes, but hopefully gets the point across. Obviously for efficiency you'd want to do this on a larger granularity than single write
calls (for example, you could block SIGPIPE
for the duration of the whole library function until it returns to the outside caller).
An alternate design would be simply blocking SIGPIPE
and never unblocking it, and documenting in the function's interface that it leaves SIGPIPE
blocked (note: blocking is thread-local and does not affect other threads) and possibly leaves SIGPIPE
pending (in the blocked state). Then the caller would be responsible for restoring it if necessary, so the rare caller that wants SIGPIPE
could get it (but after your function finishes) by unblocking the signal while the majority of callers could happily leave it blocked. The blocking code works like in the above, with the sigtimedwait
/unblocking part removed. This is similar to Maxim's answer except that the impact is thread-local and thus thread-safe.
Now I did learn how to simply handle the signal: create a method that receives a int and use signal(SIGPIPE, myMethod)
You just need to ignore SIGPIPE
, no handler is needed:
// don't raise SIGPIPE when sending into broken TCP connections
::signal(SIGPIPE, SIG_IGN);
But the problem is that by doing this way I'm unable to stop the sending of information to the dead client, for the method that handles the signal needs to be either outside the class which sends the message or a static method, which don't have access to my server object.
When SIGPIPE
is ignored writing into a broken TCP connection returns error code EPIPE
, which the socket wrappers you use should handle like the connection has been closed. Ideally, the socket wrapper should pass MSG_NOSIGNAL
flag to send
, so that send
never raises SIGPIPE
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With