5.13 SIGPIPE
Signal
What happens if the client ignores the error
return from readline and writes more data to the server?
This can happen, for example, if the client needs to perform two
writes to the server before reading anything back, with the first
write eliciting the RST.
The rule that applies is: When a process writes
to a socket that has received an RST, the SIGPIPE signal
is sent to the process. The default action of this signal is to
terminate the process, so the process must catch the signal to
avoid being involuntarily terminated.
If the process either catches the signal and
returns from the signal handler, or ignores the signal, the write
operation returns EPIPE.
A frequently asked question (FAQ) on Usenet is
how to obtain this signal on the first write, and not the second.
This is not possible. Following our discussion above, the first
write elicits the RST and the second write elicits the signal. It
is okay to write to a socket that has received a FIN, but it is an
error to write to a socket that has received an RST.
To see what happens with SIGPIPE, we
modify our client as shown in Figure 5.14.
Figure 5.14
str_cli that calls writen twice.
tcpcliserv/str_cli11.c
1 #include "unp.h"
2 void
3 str_cli(FILE *fp, int sockfd)
4 {
5 char sendline [MAXLINE], recvline [MAXLINE];
6 while (Fgets(sendline, MAXLINE, fp) != NULL) {
7 Writen(sockfd, sendline, 1);
8 sleep(1);
9 Writen(sockfd, sendline + 1, strlen(sendline) - 1);
10 if (Readline(sockfd, recvline, MAXLINE) == 0)
11 err_quit("str_cli: server terminated prematurely");
12 Fputs(recvline, stdout);
13 }
14 }
7鈥?
All we have changed is to call writen two times: the first
time the first byte of data is written to the socket, followed by a
pause of one second, followed by the remainder of the line. The
intent is for the first writen to elicit the RST and then
for the second writen to generate SIGPIPE.
If we run the client on our Linux host, we
get:
linux % tcpclill 127.0.0.1
|
hi
there
|
we type this
line
|
hi there
|
this is echoed by the
server
|
|
here we kill the
server child
|
bye
|
then we type this
line
|
Broken pipe
|
this is printed by the
shell
|
We start the client, type in one line, see that
line echoed correctly, and then terminate the server child on the
server host. We then type another line ("bye") and the shell tells
us the process died with a SIGPIPE signal (some shells do
not print anything when a process dies without dumping core, but
the shell we're using for this example, bash, tells us
what we want to know).
The recommended way to handle SIGPIPE
depends on what the application wants to do when this occurs. If
there is nothing special to do, then setting the signal disposition
to SIG_IGN is easy, assuming that subsequent output
operations will catch the error of EPIPE and terminate. If
special actions are needed when the signal occurs (writing to a log
file perhaps), then the signal should be caught and any desired
actions can be performed in the signal handler. Be aware, however,
that if multiple sockets are in use, the delivery of the signal
will not tell us which socket encountered the error. If we need to
know which write caused the error, then we must either
ignore the signal or return from the signal handler and handle
EPIPE from the write.
|