CSE 40771 - Distributed Systems - Spring 2026
To realize our final P2P file distribution system we must be able to support more than a single client and server pair. To get closer to the BitTorrent implementation our peer also must support requests from other peers to share data we have, while simultaneously downloading the data we need.
In this assignment we will implement this capability and finally have our first P2P communication (it’s about time!).
In class, we discussed several models for building servers: single-threaded (what you have already), multi-threaded, multi-process, and event-driven. We are going to use the latter model, because all operations must share the same underlying checkpoint and transaction log.
Here is how to think about it:
Your server will keep track of the master socket on which it is listening
for new connections. It will also have a dictionary of sockets, each one
representing a currently-connected client. In the server’s main loop, it
should use epoll to determine which socket is readable.
If our peer’s only job was to perform the server functionality we could block until some event happens. However our peer needs to be working on getting the data itself while also serving other peers. Thus we need to poll the server socket for activity, handle the request, and return to our work.
If we find the master socket is ready for reading, then the server should accept a new connection and add it to the set. If any other socket is ready for reading, then the server should read a request from the socket, perform the action, send a response, and return to its polling loop.
This approach strikes a balance between concurrency and complexity. The server can handle multiple clients connected at once, and can interleave multiple requests to satisfy all comers. Each request still executes one at a time, so we say that the request stream is serialized. You won’t get the performance benefit of running multiple processes/threads concurrently, but you won’t have the synchronization problems from that either.
Be sure to test your server carefully before moving on. Try various combinations of clients connecting and leaving, and ensure that the server does not crash, no matter when or how the clients connect and disconnect. Add some debugging output to assure yourself that the server is in fact interleaving requests from multiple clients.
At this point we are going to combine the client and server into the Peer, such that every member of the system is an instance of Peer. Begin by making a main program called Peer.py that does the following:
Create and start an instance of HashTableServer. The server instance should have a hash table, recover from logs, register with the name service, and handle requests.
Use HashTableClient to connect to another peer’s server using the name service. Use get_description to get they available keys and request them one at a time.
Make a unique name for each peer and its server which is used when it names files and registers with the name service to avoid conflicts.
For this assignment your solution should meet these requirements:
When a peer starts it will create a server which performs all of the existing functions.
Next, the peer will query the catalog for other peers. It will connect to one of them, send get_description, and lookup the keys it gets.
When the peer gets a file from another peer, it will insert the file into its own server’s hash table.
If the peer loses connection it will use the retry mechanism on the same peer it originally connected to.
When the peer is done downloading files it will continue to run the server, and periodically send get_description to the peer it originally got the data from. If there is new data it should request it.
The above requirements describe an interesting system. It is not far from the one we have been working with.
Previously, we started a dedicated server, then connected with a client. Subsequent clients would connect to the same dedicated server.
Now every process is a peer who contains a server. Subsequent peers may connect to any peer that was already running when it starts.
The reason for this requirement is to prevent the potential for deadlock which would otherwise occur if any two peers were able to initiate a connection with each other.
Evaluate your system in the presence of multiple peers as follows:
Peer on one of the student machines. This will register it with the name service. Get some data into the hash table using your method of choice,
such as an old client script which inserts files, or recovery from a checkpoint.python Peer.py project_name peer_name
Peer simultaneously with their own data directories:python Peer.py project_name peer_name_1 & \
python Peer.py project_name peer_name_2
Capture the output of all of the peers by appending to a file. When a peer gets a file, or it serves one to another peer it should print a message describing the event.
The output should show a mix of peers performing lookups and serving files to other peers. Have enough information in the output to piece together which peers are talking to eachother.
Perform the same test as before but in several stages. Start 1 peer, then 2, then 4. Create a diagram (text, plot, drawing) of the system and what occurred when it was running.
Please review the general instructions for submitting assignments.
Turn in all of your source code, along with a lab report titled REPORT that describes the following in detail: