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.
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):
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.
For example, here is how to add stdin to the read_set:
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.
see my Unix Help pages for information about creating a
tar file using tar.
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:
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).
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).
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.
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.
Handin
Create a tar file of a directory containing:
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.
(I should only have to type 'make' to build them)
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.