Lab 5 - Handling Multiple Clients

The purpose of this lab is to learn how to handle multiple clients in a TCP/IP server using children processes and signal handling.

For this lab, we will be looking at simple_daemon.c and simple_shell.c from the code tarball lab5_files.tgz. Download the tarball with:

wget http://www.cs.csub.edu/~eddie/cmps3620/code/lab5/lab5_files.tgz
Compile the program with the command:
make daemon

The s_daemon program is a very simple TCP/IP server that will host up to MAX_CHILDREN concurrent clients. The program name comes from a Unix naming convention which calls servers "daemons" (hence many server programs having the letter "d" in their name, such as sshd, named and so on). This is a very simple, unauthenticated, plain-text Internet daemon.

Once a client has connected, s_daemon forks a child process to handle that client. The child process then spawns a s_shell process to interact with the user. The s_shell program is a very limited shell since there is no authentication with this server. We must spawn a seperate process to interact with the user because s_daemon can only interact with one client at a time in a single-threaded or single process mode. s_daemon's child process handles the data passing over the TCP socket while the s_shell process actually processes the data and does something semi-useful.

From s_shell's perspective, it is interacting with the user via standard in and standard out, not a socket. This is because s_daemon sends the data it receives from the socket into the stdin pipe for s_shell. Likewise, s_daemon reads the data coming off s_shell's stdout pipe and sends that over the socket. This is a division of labor where the daemon focuses purely on managing sockets while the shell focuses on interacting with the user.

To start s_daemon, use the command:

./s_daemon

The program will display the port number to which daemon is bound. Open another terminal and try to telnet to the daemon. For example, if it says it is on port number 19234, the command would be:

telnet localhost 19234
To end the connection, within the telnet application either give the command "quit" or type the keyboard sequence CRTL-]. The "quit" command in s_shell will terminate the connection from the server side. The CTRL-] keyboard sequenced causes telnet to suspend the connection and give you a telnet shell which looks like telnet>. From here, you can give the command "close" to terminate the current connection from the client side. Then you can give the command "quit" to exit telnet.

To kill the s_daemon process, return to the terminal you were running it in and give the CTRL-C sequence. simple_daemon.c has a signal handler for CTRL-C which will call parent_terminate(). This function cleans up after the process before exiting.

Try running the daemon with just one telnet connection at first. Then run the daemon with multiple telnet connections at the same time (you will need one terminal for each connection). Pay attention to what is printed to the daemon terminal when children connect and disconnect.

The process flow is roughly as follows:

     s_daemon parent              s_daemon child             s_shell process
     ---------------              --------------             ---------------
     bind the socket
     listen on socket
 --> accept connection
|    fork child        ------->  dialog_with_client
 --- repeat                        create pipes
                                   spawn s_shell  -------->  read from stdin pipe
                              -->  set up select       |     process command
                             |     if socket data      |     write to stdout pipe
                             |       read socket        ---  repeat while not quit
                             |       send to s_shell
                             |     if s_shell data
                             |       read s_shell
                             |       send to socket
                              ---  repeat until error
                                     or exit

Lab Writeup

Answer the following questions as your lab writeup. Submit your answers via Moodle.
  1. Unlike vcrec.c, which could only accept a single connection at a time, simple_daemon.c can accept multiple connections at once. How does it accomplish this?
  2. Try giving a CTRL-C in a telnet terminal. Why does this not cause telnet to exit?
  3. What happens when you connect to s_daemon with multiple simultaneous telnet commands? Is there any "overlap" in the commands you type in each s_shell window (i.e. do you get the output for commands typed in another window)?
  4. Which signals will cause simple_daemon.c to exit gracefully via parent_terminate()?
  5. In dialog_with_client(), the actual work of sending and receiving data from the children processes is done. How does this code tell when you have terminated the connection by giving the s_shell command "quit"?
  6. What does the child_terminate() function do? How does it differ from the kill_child() function?
  7. Given what you have learned in this lab, how do you think the SSH daemon on Sleipnir handles an incoming SSH connection from Putty? Hint: look at the command ps x right after you've logged in to Sleipnir and note what processes you have running.