CS2200 Intro to Systems and Networks
Project 5 -
Implementing a Reliable Transport Protocol
The learning objective is to help you understand that each layer in the network protocol stack has a different purpose and to guide you through 'upgrading' the transport layer of an artificial network in order to make the entire network more reliable.
Specifically, this project was designed to:
- further expose the student to the use of threads in an
operating system, especially the network implementation.
- demonstrate how messages are segmented into packets and how
they are reassembled.
- help the student understand why a checksum is needed and
when it is used.
- have the student understand and implement the stop-and-wait
protocol with ACKnowledgements Negative (NACK) Acknowledgements
The Protocol Stack:
We have provided you with code that implements the network protocol:
| Application Layer | <--- client.c
| Transport Layer | <--- rtp.c
| Network Layer | <--- network.o
| Data Link Layer |
| Physical Layer |
- For the purpose of this project, the data link layer and the physical
layer are both implemented by the operating system and the underlying network
- We have implemented our own network layer and provided it to you as an
object file (network.o). A 64-bit version is available as network64.o, rename
this if you are on a 64 bit architecture. By implementing our own network layer,
we can artificially create packet corruption and can simplify the network programming.
- The transport layer uses the services of the network layer to provide
a specialized protocol to the application. In the "real" network stack,
the transport layer typically provides TCP or UDP services to the application
using the IP services provided by the network layer.
For this project, you will be completing the transport layer.
- The application layer represents the end user application. The application
simply makes the appropriate API calls to connect to remote hosts, send and
receive messages, and disconnect from remote hosts.
Here, we will briefly describe the code provided for this project. It is
important that you study and understand the code given to you. In the past,
we have asked students to write a large portion of this code, but we
found this to be unreasonable during the last few weeks of school. As
a compromise, we have given you this code and simply ask you to complete it.
However, YOU ARE RESPONSIBLE FOR UNDERSTANDING ALL OF THE CODE.
We have provided a diagram to show the interaction among the threads in
the program (interact.gif). You may find it
helpful to use this diagram as a reference as we walk through the code.
The client program takes two arguments. The first argument is the server
it should connect to (such as localhost or salo.cc.gatech.edu), and the second
argument is the port it should connect to (such as 4000). Thus, the
client can be run as follows:
./prj5-client salo.cc.gatech.edu 4000
The client.c program represents the application layer. It uses the services
provided by the transport layer (rtp.c). It begins by connecting to the
remote host. Look at the rtp_connect connection in rtp.c. It simply uses the
services provided by the network layer to connect to the remote host. Next,
the rtp_connect function initializes its RTP_CONNECTION structure, initializes
its send and receive queue, initializes its mutexes, starts its threads, and
returns the RTP_CONNECTION structure.
Next, the client program sends a message to the remote host using
rtp_send_message(). Sending the message could take quite some time
if the network connection is slow (imagine sending a 5MB file over a
56k modem). Thus, the rtp_send_message() message makes a copy of the
information to send, places the message into a send queue, and returns so
that the application can continue to do other things. A separate thread,
the rtp_send_thread actually sends the data across the network. It waits for
a message to be placed into the send queue, then extracts that message from
the queue and sends it.
Next, the client program receives a message from the network. What happens
if a message isn't available or the entire message has not yet been received?
The rtp_receive_message() function blocks until a message can be pulled from
the receive queue. The rtp_recv_thread actually receives packets from the
network and reassembles the packets into messages. Once it receives a message,
it places the message into the receive queue so that rtp_receive_message can
extract it and return it to the application layer.
The client program continues to send and receive messages until it is
finished. Last, the client program calls rtp_disconnect() to terminate
the connection with the remote host. This function changes the state
of the connection so that other threads will know that this connection
is dead. The rtp_disconnect() function then calls net_disconnect(),
signals the other threads, waits for the threads to finish, empties the
queues, frees allocated space, and returns.
A note on the network packets. For the purposes of this project, there are
four packet types:
- DATA - a data packet that contains part of a message in its payload.
- LAST_DATA - just like a data packet, but also signifies that it is
the last packet in a message.
- ACK - acknowledges the receipt of the last packet
- NACK - a negative acknowledgement stating that the last packet received
Part I: Segmentation of Data
When data is sent over a network, the data is chopped up into one or more
parts and sent inside packets. A packet contains information that describes
the message such as the destination of the data, the source of the data, and
the data itself! The data being sent over the network is referred to as
the 'payload'. Look in network.h; what other fields does our network packet
carry? Think about why each field is needed. How much payload data can we
fit into each packet? (Note: as with many things in this project, the packet
data structure is simplified).
(20 Points) Open rtp.c and find the packetize function. Complete
this function. Its purpose is to turn a message into an array of packets.
Hint: Remember that this is integer division. If length % MAX_PAYLOAD_LENGTH = 0
this is a special case that should be handled.
- Allocate an array of packets big enough to carry all of the data.
- Populate all the fields of the packet including the payload. Remember,
The last packet should be a LAST_DATA packet. All other packets should
be DATA packets. THIS IS IMPORTANT. The server checks for this, and it
will disconnect you if they are not filled in correctly. If you neglect
the LAST_DATA packet, your program will hang forever waiting for a response
from the server, because it is waiting on you forever to send a terminating
- The 'count' variable points to an integer. Update this integer setting
it equal to the length of the array you are returning.
- Return the array of packets.
There are several other parts of the source code that say FIX ME! The
code to be inserted in these parts of the program will simply provide
additional functionality but are not necessary at this time. We will
return to these parts of the code in Part II.
Part II: When things go wrong.
In the stop-and-wait protocol, the sending thread does the following things:
The receiving thread should:
- Sends one packet at a time.
- After each packet, wait for an ACK or a NACK to be received.
- If a NACK is received, resend the last packet. Otherwise, send
the next packet.
(Part A, 20 Points) Open rtp.c and find the checksum function.
Complete this function. Simply sum the ASCII values of each character in the
buffer and return the sum. This is how the server computes the checksum
and the server and client must compute the checksum the same way.
- Compute the checksum for each packet payload upon arrival.
- If the checksum does not match the checksum reported in the
packet header, send a NACK. If it does match, send an ACK.
(Part B, 30 Points) Open rtp.c and find the rtp_recv_thread
function. If the packet is a DATA packet, the payload is added to the
current buffer. Modify the implementation so that the data is only added
to the buffer if the checksum of the data matches the checksum in the
packet header. Next, implement the code that will signal the sending
thread that a NACK or ACK has been received. You will also need to
determine a way to tell the sending thread whether a negative or
positive acknowledgement was received. (Hint: it's ok to add fields to
the RTP_CONNECTON data structure).
(Part C, 30 Points) Open rtp.c and find the rtp_send_thread
function. Find the line that says /* FIX ME */. At this point, you should
wait to be signaled by the receiving thread that a NACK or ACK has been
received. Once notified, take the appropriate action. You should
NOT call net_receive_packet in the send thread. The
receiving thread is responsible for receiving packets.
About running the server
The general server syntax is ./prj5-server [port] [corruption] [stop-and-wait].
The server has two different protocols that it can use. The first one is to not use stop and wait.
./prj5-server 4000 0.0 0
That would run the server on port 4000 not using stop and wait with no corruption. To enable corruption change the 0.0 to 0.3 for 30%.
To use the stop and wait protocol:
./prj5-server 4002 0.0 1
Again, to enable packet corruption simply change the 0.0 to 0.3 for 30%.
Try not to start late on this project. It is important you understand these
concepts for the final examination. It is also important to note that we will be grading with the stop and wait and corruption enabled.