Building Network Servers
Let's see how our abstractions hold up when we go outside the boundary of a single computer.
Learning Objectives
- Modify an existing server to efficiently handle multiple connections
- Apply what we learned about synchronization to manage our server
Get the Code
We're working in the l25
directory of the cs61-exercises
directory.
Putting our server under load
When we left our serviceserver (form the video), we used serviceserver-02, which looped handling a single request at a time. Real servers need to handle many simultaneous requests. Our work today will walk us through some different approaches to handling multiple connections simultaneously.
The serviceserver.c
file in the l25
directory is the version that
handles one connection at a time. Run it and put it in the background.
Then fire up the serviceblaster
program. Notice how slowly connections
are handled. Kill the blaster and then kill the server you have running
in the background.
A Process per Connection
One way we might handle multiple simultaneous connections would be to have the main server thread repeatedly listen on the accepting socket, but then fork off a child thread to handle each request.
Copy the serviceserver.c
file into serviceserver-process.c
. Make the
following changes to that file (and add serviceserver-process
to the
Makefile on the SERVER_PROGRAMS
line).
First, we want the server to automatically reap the child processes that
it's going to spawn, so add the line:
signal(SIGCHLD, SIG_IGN);
at the beginning of main.
Now, rather than calling handle_connection in your main server, modify
the code to fork a new child that runs only as long as it takes to
handle the connection (i.e, it should exit after it processes the
connection message). When that's working, try running the
serviceblaster
client against that server.
1. How does its behavior compare to the original serviceserver
?
A Thread per Connection
OK, so a process per connection is too resource intensive. What about threads! After all, we did just learn how to use pthreads. Let's give it a try! Rather than forking off a process per connection, let's just fork off a thread and handle the request. Implement this in a field called
serviceserver-thread.c
2. What happens?
Thread Pools (1)
One way we limit resource usage is to limit the number of concurrent
threads we have active at any time. Use what you've learned about
synchronization to impose a limit on the total number of threads the
server might have active simultaneously. Don't change your overall
structure though -- create a new thread when you can and have a thread
exit after it handles a request. Name this version of the server
serviceserver-pool.c
.
3. How well does that perform?
Wrapping Up
Please take a minute to fill out this survey.