CS 555 -- Fall 2003

Programming Assignment 1

Due: Thursday, 10/29 before 11:59PM



Source files are posted here.
Answers to frequently asked questions will be posted here.
You may discuss this assignment with other students in the class, but the work you submit must be your own. In particular, all programming efforts must be conducted individually and all code must be original (we will run automated tests to verify originality of submitted assignments). Assignments submitted before 11:59PM on 10/29 will be graded out of 100 points, assignments submitted before 11:59 on 11/1 will be graded out of 80 points, assignments submitted later will not be graded.

Overview

In this programming assignment, you will implement a simple, reliable, sequenced message transport protocol (SRMP) on top of UDP. Recall that UDP provides point-to-point, unreliable datagram service between a pair of hosts. In this programming assignment, we will develop a more structured protocol which ensures reliable, end-to-end delivery of messages in the face of packet loss, preserves ordering of transmitted messages, and preserves message boundaries.

Unlike TCP, SRMP preserves message boundaries, delivering entire messages to the application, and thus is not a logical bytestream. Also, SRMP will not provide congestion control mechanisms, although it will support window-based flow control using a receiver-advertised window. Finally, whereas TCP allows fully bidirectional communication, our implementation of SRMP will be asymmetric. The two SRMP endpoints will be designated the "sender" and the "receiver" respectively. Data packets will only flow in the "forward" direction from the sender to the receiver, while acknowledgments will only flow in the "reverse" direction from the receiver back to the sender.

To support reliability in a protocol like SRMP, state must be maintained at both endpoints. Thus, as in TCP, connection set-up and connection teardown phases will be an integral part of the protocol.

As we mentioned earlier, congestion control will not be a part of SRMP. However, this does not imply that a sender can transmit as fast as it pleases. SRMP uses window-based flow control whereby the receiver regulates the sender by transmitting an advertised window (as in TCP) which bounds the amount of data SRMP can inject into the network at any point in time.

SRMP

Packet Headers

SRMP packets have 3 fields, as specified in the following structure.

   typedef struct {
      unsigned short type;
      unsigned short window;
      unsigned short seqno;
   } srmphdr;
The "type" field takes on 5 possible values. DATA = 0, ACK = 1, SYN = 2, FIN = 3, RESET = 4. Unlike TCP, in which multiple types can be set simultaneously, SRMP packets must be of exactly one of the types specified above.

The "window" field specifies the receiver's advertised window in bytes and is used only in packets of type 1 (ACK). The window field must be set to zero for all packets of other types. The maximum window size is 2^16 - 1.

The "seqno" field indicates the sequence number of the packet. This field is used in all packets except RESET packets, when it is set to zero. For DATA packets, the sequence number increases by the size (in bytes) of each packet. For ACK packets, the sequence number acts as a cumulative acknowledgment, and indicates the number of the next byte expected by the receiver. For SYN packets, the sequence number is one smaller than the sequence number of the first data packet of the connection. For FIN packets, the sequence number is one larger than the sequence number of the last byte of the last data packet of the connection. The maximum sequence number is 2^16 - 1 and the maximum message size in SRMP is 1000 bytes.

State Diagram

The asymmetry between sender and receiver in this protocol leads to somewhat different state diagrams for the two endpoints. The state diagram for SRMP is as follows:

The receiver can be in four possible states: CLOSED, LISTEN, ESTABLISHED and TIME_WAIT. Initially, it is in the CLOSED state. Upon issuing a passive open, it enters the LISTEN state. (Note that the receiver is the passive host in our protocol, while the sender actively opens the connection). While in the LISTEN state, the receiver waits for a SYN packet to arrive on the correct port number. When it does, it responds with an ACK, and moves to the ESTABLISHED state. After the sender has transmitted all data (and received acknowledgments), it will send a FIN packet to the receiver. Upon receipt of the FIN, the receiver moves to the TIME_WAIT state. As in TCP, it remains in TIME_WAIT for two maximum segment lifetimes (MSLs) before re-entering the CLOSED state. Assume for this assignment that the MSL is 2 seconds.

The sender can be in five possible states: CLOSED, SYN_SENT, ESTABLISHED, CLOSING and FIN_WAIT. Like the receiver, the sender starts in the CLOSED state. It then issues an active open by sending a SYN packet (to the receiver's port), thus entering the SYN_SENT state. This SYN transmission also includes the inital sequence number (ISN) of the conversation. The ISN should be chosen at random from the valid range of possible sequence numbers. Any packet (including the SYN packet) which is not acknowledged in 2 seconds must be retransmitted. If the SYN packet is not acknowledged after three attempts, a RESET packet must be sent to the destination port and the sender moves to the CLOSED state. In the common case in which the SYN is acknowledged correctly (the ACK must have the correct sequence number = ISN + 1), the sender enters the ESTABLISHED state and starts transmitting packets. When the sending application (sitting above SRMP) is finished generating data, it issues a "close" operation to SRMP. This causes the sender to enter the CLOSING state. At this point, the sender must still ensure that all buffered data arrives at the receiver reliably. Upon verification of successful transmission, the sender sends a FIN packet with the appropriate sequence number and enters the FIN_WAIT state. Once the FIN packet is acknowledged, the sender re-enters the CLOSED state. If no ACK comes after three timeouts (which are also 2 seconds each), the sender should send a RESET packet and return to the CLOSED state.

In the event that one party detects that the other is misbehaving, it should reset the connection by sending a RESET packet. For example, one easy way in which a receiver can misbehave is to acknowledge a packet that has not yet been transmitted.

Implementation

We have provided an implementation of the SRMP receiver process in C; you must write the sender. You may elect to build your sender from a skeleton that we provide (in C), or you can build your own from scratch in any language you choose including C++ or Java (implementation in Java will require some extra work to re-implement utility functions we provide). In the skeleton we provide, applications will invoke the sender's functionality by means of the following application programmer's interface (API) consisting of the following three routines: The first function is called once by the application at the sending side to open and establish an SRMP connection to a remote receiver. The second function is called repeatedly by the application to transmit messages across an established connection, and the final function is used by the application when it has no more messages to transmit, and wishes to teardown the connection. To manage your protocol, you will need a global and central "structure" or "object" to help you keep track of things like state transitions, sliding windows and buffers. On the receiver's side, we refer to this structure as srmp_recv_ctrl_blk (defined in srmp.h). In the sender skeleton, we refer to this structure as srmp_send_ctrl_blk. Feel free to use the receiver control block as a reference for your design

While a typical protocol implementation would consist of many threads of control, we will simplify things by using a single-threaded approach to implementing the sender's functionality. One possible decomposition of srmp_send() might be as follows:

By continuously calling srmp_send(), the data transfer will proceed smoothly. Eventually, when the application has no more data to transmit, it calls srmp_close(). At this point, your SRMP connection must make sure all buffered is transmitted and acknowledged before tearing down the connection.

The makefile builds a simple file transfer application which consists of two executables. "SendApp" is an application which takes three command line arguments: a destination address (da), a destination port (dp), and a source port (sp). The application uses the send-side API to call the srmp_ routines you will write to transfer the contents of a file to a remote receiver. On the other side of the connection, "ReceiverApp" is an application which expects to be sent a file, and uses the receive-side API to call the srmp_routines (provided) to get the file, then writes it to a file called "TestOutput".

To recap, you can build "ReceiveApp" with the Makefile and these source files (all available):

by typing "make ReceiveApp". Once you've finished creating sender.c (we recommend building it from the template provided), type "make SendApp".

Testing

To test your code, we will provide several receivers for you to test your sender against (available on the homepage). Source code will be provided for a basic receiver which has a small, fixed advertised window and does not lose packets. Correct behavior against this receiver will be worth approximately 3/4 of the credit. More complex receivers which vary their window size and simulate a network which reorders and drops packets are also avaiable for you to test against. You should do all (or almost all) of your testing in a local environment without going over the network. By routing packets to "localhost", those packets are sent a special software interface known as the loopback interface, which redirects the packets immediately back to the localhost. You can run your receiver in one window:
 % ./ReceiveApp localhost 4562 4182
then run the sender in another window:
 % ./SendApp localhost 4182 4562 SendFileName
(reversing the source and destination port numbers so the programs plug together naturally). SendFileName is the name of file the application will send.

Simplifications

You need not worry about the following aspects of the implementation. On the other hand, don't oversimplify. You will need to deal with: This programming assignment will be time-consuming, but will make important concepts we covered in class much more familiar to you. Please make sure to allocate enough time to be able to submit a nicely debugged assignment.

How to Proceed

Large assignments such as this can be daunting and it can be hard to figure out where to start. In general, implement the solution piece by piece, testing thoroughly as you go. Here are some suggestions:

Submission and Grading Guidelines

You are to electronically submit the code and Makefile for the sender that you write by using "gsubmit". Details for submission will be provided when the deadline approaches. We will be running and testing your code on the csa machines -- make absolutely sure that your program compiles and runs correctly on csa well before the deadline -- there will be portability issues if you develop your code on another platform, and these may take considerable time to resolve! To calibrate, programs which do not compile will be worth nothing; programs which run, but are unable to reliably transfer a small file against the basic receiver will get at most 30 percent of the credit.

Academic Conduct Reminder

Submitting part or all of another person's assignment (or leveraging another person's work to produce your own submission) is an act of plagiarism. Submissions will be checked for originality by hand and by a program checker -- please do not jeopardize your academic standing by taking chances in completing this assignment. Please come see me if you have any questions about where I (and the college) draw the line regarding acceptable levels of collaboration.