The server that was developed in previous posting Using Standard I/O on Sockets has been modified in this section to handle multiple clients by means of a fork(2) system call. Listing 11.2 shows the listing of the modified rpnsrv.c module. All other source modules remain the same as they appeared in the previous post.
The following session shows how to compile and to start the server in the background:
[sgupta@rhel54x64 rpn-main-server]$ gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpnsrv.c [sgupta@rhel54x64 rpn-main-server]$ gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpneng.c [sgupta@rhel54x64 rpn-main-server]$ gcc -c -D_GNU_SOURCE -Wall -Wreturn-type mkaddr.c gcc [sgupta@rhel54x64 rpn-main-server]$ rpnsrv.o rpneng.o mkaddr.o -o rpnsrv -Igmp
[sgupta@rhel54x64 rpn-main-server]$ ./rpnsrv '*:9090' &  915
After the server has been started, you can use telnet from multiple xterm windows to try out the server simultaneously. If you are not running the X Window system, you can use various virtual console sessions to accomplish the same effect.
The principle changes to the module are as follows:
Understanding Process Termination Processing
The one complication that the fork(2) function call inflicts upon the design of the server is that it must process information about terminated processes. This is very important, because when a child process terminates, most of its resources are released. The rest of its resources are released only when the parent process obtains the child process termination status information.
The parent process is notified of a child process termination by means of the signal SIGCHLD. Now examine the steps that the parent server process uses when a child process terminates:
- The signal SIGCHLD is raised by the kernel to indicate that the child process has terminated.
- The function sigchld_handler() is called (line 35), because the function was registered for the SIGCHLD signal.
- The sigchld_handler() executes a loop calling waitpid(2) until no more exit status information is available.
- The SIGCHLD handler is re-instated. This was necessary because the reliable signals interface was not used in order to keep the example program simple.
In a production mode server, only the reliable signal functions such as sigaction (2) should be used. This was avoided in the example program to keep the source code simple.
Failure to call wait(2) or waitpid(2) by the parent process after a fork(2) and the child process's subsequent termination will result in zombie processes being left around until the parent process terminates. This can tie up valuable system resources.
The reader is encouraged to review the functions fork(2), waitpid(2), and signal(2), if necessary. These are important aspects of this server design.