The purpose of this section has been to demonstrate three scenarios that we can encounter with network programming:
1.We must catch the SIGCHLD signal when forking child processes.
2.We must handle interrupted system calls when we catch signals.
3.A SIGCHLD handler must be coded correctly using waitpid to prevent any zombies from being left around.
Connection Abort before ‘accept’ Returns
There is another condition similar to the interrupted system call example in the previous section that can cause accept to return a nonfatal error, in which case we should just call accept again.
Termination of Server Process
We will now start our client/server and then kill the server child process. This simulates the crashing of the server process, so we can see what happens to the client.
The following steps take place:
1.We start the server and client and type one line to the client to verify that all is okay. That line is echoed normally by the server child.
2.We find the process ID of the server child and kill it. As part of process termination, all open descriptors in the child are closed. This causes a FIN to be sent to the client, and the client TCP responds with an ACK. This is the first half of the TCP connection termination.
3.The SIGCHLD signal is sent to the server parent and handled correctly (Figure 5.12(See 8.3.10)).
4.Nothing happens at the client. The client TCP receives the FIN from the server TCP and responds with an ACK, but the problem is that the client process is blocked in the call to fgets waiting for a line from the terminal.
5.Running netstat at this point shows the state of the sockets.
6.We can still type a line of input to the client.
7.When we type “another line,” str_cli calls writen and the client TCP sends the data to the server. This is allowed by TCP because the receipt of the FIN by the client TCP only indicates that the server process has closed its end of the connection and will not be sending any more data. The receipt of the FIN does not tell the client TCP that the server process has terminated (which in this case, it has).
7.When the server TCP receives the data from the client, it responds with an RST since the process that had that socket open has terminated. We can verify that the RST was sent by watching the packets with tcpdump.
8.The client process will not see the RST because it calls readline immediately after the call to writen and readline returns 0 (EOF) immediately because of the FIN that was received in Step 2. Our client is not expecting to receive an EOF at this point (Figure 5.5(See 8.3.5)) so it quits with the error message “server terminated prematurely.”
9.When the client terminates (by calling err_quit in Figure 5.5(See 8.3.5)), all its open descriptors are closed.
‘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.