Exercise #13

Out: Wednesday December 4, 2013
Due: Friday December 6, 2013 by 11:15am.


You're given code for a client and a server. The server waits for a client connection, then reads whatever the client sends it and sends it back, i.e., it echos. The client reads the lines of a text file and sends them one by one to the server. Both the client and the server print information about their activity to stdout.

There are two problems with the existing code, though:

  1. The server is not multithreaded, so a single thread must handle both waiting for new client connections and interacting with a connected client. That means new clients are unable to connect to the server while it's already busy with some other client.

  2. Terminating execution of the server is difficult, as it has no UI. To terminate, we send it a "signal." When it receives the signal, it sets a flag indicating that it's time to terminate. When a thread sees that flag is set, it terminates itself. Unfortunately, when a thread tries to read from a socket, it will block until some new data arrives on it, which could take a very long time, and so it may not notice that the flag has been set. To help terminate promptly, the server code sets timeouts on the sockets -- if no activity happens within a fairly short amount of time, a read(), say, returns anyway. These timeouts are an opportunity for the threads to check the termination flag, and terminate if necessary. (If not, they just try doing the read() again, basically.)

    The full implementation of this mechanism is in the code for the server socket, the one that accepts connections. It is partly missing for the sockets that are connected to clients.

Run the Code

  1. Fetch the skeleton code, ex13.tar.gz, expand it, and build it by saying make.
  2. Run it. Start a server:
    $ ./ex13-server
    
    Note the pid (process id) and port number that it prints when it starts.

    In another window, start a client:

    $ ./ex13-client datafiles/data1.txt 127.0.0.1 <server port number>
    
  3. Optionally, try connecting a second client. It will hang.
  4. Kill the server gracefully by sending it a signal:
    $ kill -s SIGUSR1 <server pid>
    
Note: Roughly, only one process can hold a given port number at a time. So, if you're running on a shared machine like attu and you don't change the port number that has been hardcoded into the server, you might conflict with someone else who also didn't change it. You should both change it. (It's set at the top of the server code. Just pick a number in the range 10,000 to 20,000, say.)

Fix the Code

  1. Change the code so that it spawns a new thread to handle each new client connection. This requires approximately 1/2 line of code. The example on this page shows you what to do, and the comments in the code show you where to do it.
  2. Modify the code to correctly deal with timeouts that might occur on the sockets connected to clients. This requires basically reading and understanding the code that deals with timeouts on the server socket and then applying it to the client connection sockets.

Verify that your modifications work by connecting two clients to your server at the same time.

Note: You can use nc (man nc) to test your server:

$ nc 127.0.0.1 <server port number>
will connect you to the server as a client. Whatever you type should be echoed back to you. If you fail to type anything, the server's client connection socket will time out.

Turn-in

You should submit your server code file to the course dropbox.