CS 85: Distributed Systems, Spring 2008, Swarthmore College, Professor Newhall

Old Skool Instant Messaging part 2

Due: Thursday, Feb. 14 before 11am



Project description
Project Details including some useful system calls
What to Submit and Demo

Project Description

For this assignment, you will extend your talk client and server programs from homework 1, in the following ways:
  1. The server will be able to support multiple, simultaneous talk sessions with different clients
  2. The client and server no longer have to alternate sending and receiving messages. For example, the client could send multiple lines of user input to the server before the server responds with a line of input that it sends to the client.

To add support for multiple simultaneous connections, the server process will create a separate helper process to handle each client connection. The helper process will be dedicated to communicating with one particular client. The main server process, after creating the helper process, will go back to its main listening loop and wait to accept a connection from another client, spawn another helper process to handle that connection, and so on.

To add support for removing the strict ordering between a client and server's sends and receives, you will need to use the select system call that allows a process to wait for input on a set of file descriptors (one for the socket, and one for standard input from the user). When, data is ready to read on any of the set of file descriptors, select returns, and the the process can read in the data and handle it appropriately; if data is ready to read on stdin, it reads a line of input from the user and sends it to the other end of the TCP connection, and if data is ready to read on the socket, it reads the message from the other end, and handles it appropriately.

Project Details

Start by copying over your solution to homework 1 into a new directory for this project (leave your working homework 1 solution as is).

Support for Multiple Simultaneous Connections

Most real-world servers support multiple simultaneous connections. Typically, there is a main listener thread or process that sits in a loop:
  1. wait to accept the next connection from a client
  2. create a thread or process to be the dedicated server-end for the connection with this newly connecting client (making sure to set up all state necessary for the new server thread or process to do so)
  3. return to its mainloop, ready to accept the next client connection
To create a new process, you need to call the fork system call. fork creates a new process that gets a copy of its parent' s address space (both the child and parent process continue at the instruction immediately following the call to fork. In the child process, fork returns 0, in the parent process, fork returns the process ID of the child (which will never be 0). For example:
  int child_pid = fork();
  if(child_pid == -1) {
    // fork failed...handle this error

  } else  if(child_pid == 0) {
    // child process will execute code here 
    ...

  } else {
    // parent process will execute this code
    ...
  }
When a new process is created, the child process gets a copy of all its parent's state at the point of the call (e.g. same stack, hence continuing at the same point in the code, and same set of open files, hence the parent and child share the same open sockets, they share stdin, stdout, stderr, and any other open files). One thing that this means is that the parent and child process should each close all file descriptors that they don't need (e.g. the parent should close its copy of the fd returned by accept and the client should close its copy of the listen socket fd).

Typically, the server handler code is part of the server executable program, and the child, after closing its copy of the listener socket, just calls the correct server handler function to communicate with its client (since it gets a copy of it's parent's address space, it has this function). However, for the talk server there is a problem in using this model, because every handler child process will share the same stdin, which means that there is only a single server input stream for the multiple, simultaneous connections. Instead, we want each server-side handler process to have its own separate input stream for its dedicated connection to a client. To do this, you will have the child process exec the xterm program (use the execvp system call). This will pop up a new window from which this process' stdin will come. exec overlays the calling process' address space with a new executable program and starts executing that program from its start point (main). When you exec the xterm you should do so with the command line argument -e that specifies another executable to exec (this will be an executable consisting of a server handler program that will contain your code for handling communication with the remote client process). You will need to pass this executable the value of the socket file descriptor as a command line argument (exec will overwrite the calling process' address space, so the variable used to store the socket fd returned from accept, is no longer in the child process' address space after the call to exec). Here is an example of what the command you want to exec may look like (remember that the socket file descriptor could have any value, not necessarily 3):

xterm -e server_handler 3
This will create an xterm that execs the server_handler program with the single command line argument 3 (which in this example is the value of the file descriptor of the socket for communicating with the remote client process).

After the fork, the child process will call execvp, to execute the above command. You can use C string library and sprintf to to create the correct string args for each call to excvp.

You will need to build a new executable program for the server handler that takes a single command line argument which is the file descriptor value of the socket. Edit the makefile to add a command to build this.

Removing Alternating send/receive ordering between client and server

To remove the fixed alternating send/receive ordering between client and server side, you will need to use the select system call to specify that a process is ready to read data from any one of a set of file descriptors:
select(num_fds, &read_set, 0, 0, 0);
For this project the read set to pass to select will consist of file descriptor 0 (for reading in input typed in from the user), and the socket file descriptor (for reading a message from the other end of the socket). If there are data to read on either end, select will return. You can then test which file descriptor(s) are ready to read from, and read from it (them) and perform the appropriate action. Use the macros FD_ZERO and FD_SET to set the read_set each time before you call select. The macro FD_ISSET, can be used to test which file descriptor(s) have data to read.

For example, here is how to add stdin to the read_set:

 FD_ZERO(&read_set);
 FD_SET(0, &read_set);
You should use the same protocol you used in homework 1 for exchanging messages between client and server processes. For example, if in your homework 1 solution, the client sent the server a message tag first, then it should continue to do so in this version.

Again, use man pages, and the socket programming references for help: Network Programming Links

For this assignment, you are not required to handle signals, nor do you need to do any fancy windowing beyond exec'ing the xterm which execs your server handler program for each connection. If you are interested in trying to add more functionality, you could look at ncurses for windowing stuff and look at the man page for sigaction for better signal handling.

Handin

Create a tar file of a directory containing:
  1. a README file with (1) both your names (2) information about how to run your programs (3) anything else you want to tell me about your solution
  2. a Makefile for building your client and server programs
  3. all the source and header files I need to build your programs:
    (I should only have to type 'make' to build them)
Email me your tar file as an attachment sometime before the due date. You may want to cc yourself to make sure that what you sent me is your solution.

see my Unix Help pages for information about creating a tar file using tar.

Demo

We will devote ~20 at the end of class on Thursday to have everyone run and try out each others' solutions in the overflow Lab.
Except as otherwise noted, the content of this presentation is licensed under the Creative Commons Attribution 2.5 License.