天天看点

Unix Network Programming Episode 38

Normal Termination

At this point, the connection is established and whatever we type to the client is echoed back.

The client’s side of the connection (since the local port is 42758) enters the TIME_WAIT state (Section 2.7(See 7.2.7)), and the listening server is still waiting for another client connection. (This time we pipe the output of netstat into grep, printing only the lines with our server’s well-known port. Doing this also removes the heading line.)

We can follow through the steps involved in the normal termination of our client and server:

1.When we type our EOF character, fgets returns a null pointer and the function str_cli (Figure 5.5(See 8.3.5)) returns.

2.When str_cli returns to the client main function (Figure 5.4(See 8.3.4)), the latter terminates by calling exit.

3.Part of process termination is the closing of all open descriptors, so the client socket is closed by the kernel. This sends a FIN to the server, to which the server TCP responds with an ACK. This is the first half of the TCP connection termination sequence. At this point, the server socket is in the CLOSE_WAIT state and the client socket is in the FIN_WAIT_2 state (Figures 2.4(See 7.2.6) and 2.5(See 7.2.6)).

4.When the server TCP receives the FIN, the server child is blocked in a call to readline (Figure 5.3(See 8.3.4)), and readline then returns 0. This causes the str_echo function to return to the server child main.

5.The server child terminates by calling exit (Figure 5.2(See 8.3.2)).

6.All open descriptors in the server child are closed. The closing of the connected socket by the child causes the final two segments of the TCP connection termination to take place: a FIN from the server to the client, and an ACK from the client (Figure 2.5(See 7.2.6)). At this point, the connection is completely terminated. The client socket enters the TIME_WAIT state.

7.Finally, the SIGCHLD signal is sent to the parent when the server child terminates. This occurs in this example, but we do not catch the signal in our code, and the default action of the signal is to be ignored. Thus, the child enters the zombie state. We can verify this with the ps command.

POSIX Signal Handling

A signal is a notification to a process that an event has occurred. Signals are sometimes called software interrupts. Signals usually occur asynchronously. By this we mean that a process doesn’t know ahead of time exactly when a signal will occur.

Signals can be sent

  • By one process to another process (or to itself)
  • By the kernel to a process

The SIGCHLD signal that we described at the end of the previous section is one that is sent by the kernel whenever a process terminates, to the parent of the terminating process.

1.We can provide a function that is called whenever a specific signal occurs. This function is called a signal handler and this action is called catching a signal. The two signals SIGKILL and SIGSTOP cannot be caught. Our function is called with a single integer argument that is the signal number and the function returns nothing. Its function prototype is therefore

void handler (int signo);
           

2.We can ignore a signal by setting its disposition to SIG_IGN. The two signals SIGKILL and SIGSTOP cannot be ignored.

3.We can set the default disposition for a signal by setting its disposition to SIG_DFL. The default is normally to terminate a process on receipt of a signal, with certain signals also generating a core image of the process in its current working directory. There are a few signals whose default disposition is to be ignored: SIGCHLD and SIGURG (sent on the arrival of out-of-band data, Chapter 24(See 9.13)) are two that we will encounter in this text.