A quite standard C++ TCP server program using pthreads, bind, listen and accept. I have the scenario that the server ends (read: crashes) when I kill a connected client.
The reason for the crash is that the write()
call on the file fails, thus the program receives a SIGPIPE. And I guess, this makes the server exit.
I thought, "of course, unhandled signal means exit", so let's use signal()
:
signal(SIGPIPE, SIG_IGN);
because, taken from man 2 write
:
EPIPE fd is connected to a pipe or socket whose reading end is closed. When this happens the writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)
Alas, no. Neither in the server thread nor the client threads does this seem to help.
So, how do I prevent the write()
call from raising that signal, or (to be pragmatic) how do I stop the server from exiting.
My diagnostics are:
pkill telnet
to crash the clientunwanted behavior: server exits, in gdb with
... in write () at ../sysdeps/unix/syscall-template.S:82
82 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
and the backtrace:
#0 ... in write () at ../sysdeps/unix/syscall-template.S:82
#1 ... in ClientHandler::mesg(std::string) ()
#2 ... in ClientHandler::handle() ()
#3 ... in start_thread (arg=<value optimized out>) at pthread_create.c:300
#4 ... in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#5 ... in ?? ()
You generally want to ignore the SIGPIPE and handle the error directly in your code. This is because signal handlers in C have many restrictions on what they can do. The most portable way to do this is to set the SIGPIPE handler to SIG_IGN . This will prevent any socket or pipe write from causing a SIGPIPE signal.
Another cause of SIGPIPE is when you try to output to a socket that isn't connected. See Sending Data. Resource lost. This signal is generated when you have an advisory lock on an NFS file, and the NFS server reboots and forgets about your lock.
Late to the party, but just wanted to add to this for future reference: If you are debugging your code in gdb, don't forget that it overrides your signal handlers.
So if you have set a signal handler such as: signal(SIGPIPE, SIG_IGN) and it doesn't seem to be working, try running the code outside the debugger.
Or set handle SIGPIPE nostop
(in gdb prompt) to prevent gdb stopping on the signal.
Did you by any chance not do the signal
ignore prior to spawning off any threads? If you waited until later one of the other threads could still pick up the signal and exit your app.
If that doesn't do it, you can always do a write poll
/select
before trying the write to make sure the socket is writable.
When you ignore SIGPIPE, you no longer get a SIGPIPE signal, but write()
gets a EPIPE
error.
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