CS2200 Intro to Systems and Networks
Project 5 - Implementing a Reliable Transport Protocol

Learning Objective:

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:

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    |

Code Walk-Through:

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:

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. It should:
  1. Allocate an array of packets big enough to carry all of the data.
  2. 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 packet.
  3. The 'count' variable points to an integer. Update this integer setting it equal to the length of the array you are returning.
  4. Return the 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.

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:
  1. Sends one packet at a time.
  2. After each packet, wait for an ACK or a NACK to be received.
  3. If a NACK is received, resend the last packet. Otherwise, send the next packet.
The receiving thread should:
  1. Compute the checksum for each packet payload upon arrival.
  2. If the checksum does not match the checksum reported in the packet header, send a NACK. If it does match, send an ACK.
(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.

(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%.

Good luck

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.