Programming Assignment:
Implementing a Reliable Transport Protocol

 


Overview

In this programming assignment, you will be writing the sending and receiving transport-level code for implementing a simple reliable data transfer protocol. The basic assignment will be to implement the Alternating Bit Protocol; the extra credit assignment will be to implement a Go-Back-N protocol. This should be FUN since your implementation will differ very little from what would be required in a real-world situation.

Since we do not have standalone machines (with an OS that you can modify), your code will have to execute in a simulated hardware/software environment. However, the programming interface provided to your routines (i.e., the code that would call your entities from above (i.e., from layer 5) and from below (i.e., from layer 3)) is very close to what is done in an actual UNIX environment. (Indeed, the software interfaces described in this programming assignment are much more realistic that the infinite loop senders and receivers that the text describes). Stopping/starting of timers are also simulated, and timer interrupts will cause your timer handling routine to be activated.

The desciption of the routines below is given in C.  See text at end regarding the Java version of this assignment. Note that you do not need a network connection to run this assignment, so you can do it pretty much on any machine you would like.

The routines you will write The procedures you will write are for the sending entity (A) and the receiving entity (B). Only unidirectional transfer of data (from A to B) is required. Of course, the B side will have to send packets to A to acknowledge (positively or negatively) receipt of data. Your routines are to be implemented in the form of the procedures described below. These procedures will be called by (and will call) procedures that I have written which emulate a network environment. The overall structure of the environment is shown in Figure 1:

The unit of data passed between the upper layers and your protocols is a message, which is declared as:

struct msg {
  char data[20];
  };
This declaration, and all other data structure and emulator routines, as well as stub routines (i.e., those you are to complete) are in the file, prog2.c, described later. Your sending entity will thus receive data in 20-byte chunks from layer5; your receiving entity should deliver 20-byte chunks of correctly received data to layer5 at the receiving side.

The unit of data passed between your routines and the network layer is the packet, which is declared as:

struct pkt {
   int seqnum;
   int acknum;
   int checksum;
   char payload[20];
    };
Your routines will fill in the payload field from the message data passed down from layer5. The other packet fields will be used by your protocols to insure reliable delivery, as we've seen in class.

The routines you will write are detailed below. As noted above, such procedures in real-life would be part of the operating system, and would be called by other procedures in the operating system.

A_output(message),
where message is a structure of type msg, containing data to be sent to the B-side.

This routine will be called whenever the upper layer at the sending side (A) has a message to send. It is the job of your protocol to insure that the data in such a message is delivered in-order, and correctly, to the receiving side upper layer.
A_input(packet),
where packet is a structure of type pkt.

This routine will be called whenever a packet sent from the B-side (i.e., as a result of a tolayer3() being done by a B-side procedure) arrives at the A-side. packet is the (possibly corrupted) packet sent from the B-side.
A_timerinterrupt()
This routine will be called when A's timer expires (thus generating a timer interrupt). You'll probably want to use this routine to control the retransmission of packets. See starttimer() and stoptimer() below for how the timer is started and stopped.
A_init()
This routine will be called once, before any of your other A-side routines are called. It can be used to do any required initialization.
B_input(packet),
where packet is a structure of type pkt.

This routine will be called whenever a packet sent from the A-side (i.e., as a result of a tolayer3() being done by a A-side procedure) arrives at the B-side. packet is the (possibly corrupted) packet sent from the A-side.
B_init()
This routine will be called once, before any of your other B-side routines are called. It can be used to do any required initialization.


Software Interfaces

The procedures described above are the ones that you will write. I have written the following routines which can be called by your routines:

starttimer(calling_entity,increment),
where calling_entity is either 0 (for starting the A-side timer) or 1 (for starting the B side timer), and increment is a float value indicating the amount of time that will pass before the timer interrupts. A's timer should only be started (or stopped) by A-side routines, and similarly for the B-side timer.

To give you an idea of the appropriate increment value to use: a packet sent into the network takes an average of 5 time units to arrive at the other side when there are no other messages in the medium.
stoptimer(calling_entity),
where calling_entity is either 0 (for stopping the A-side timer) or 1 (for stopping the B side timer).
tolayer3(calling_entity,packet),
where calling_entity is either 0 (for the A-side send) or 1 (for the B side send), and packet is a structure of type pkt.

Calling this routine will cause the packet to be sent into the network, destined for the other entity.
tolayer5(calling_entity,message),
where calling_entity is either 0 (for A-side delivery to layer 5) or 1 (for B-side delivery to layer 5), and message is a structure of type msg. With unidirectional data transfer, you would only be calling this with calling_entity equal to 1 (delivery to the B-side).

Calling this routine will cause data to be passed up to layer 5.
The simulated network environment

A call to procedure tolayer3() sends packets into the medium (i.e., into the network layer). Your procedures A_input() and B_input() are called when a packet is to be delivered from the medium to your protocol layer.

The medium is capable of corrupting and losing packets. It will not reorder packets. When you compile your procedures and my procedures together and run the resulting program, you will be asked to specify values regarding the simulated network environment:

The Basic Assignment

You are to write the procedures, A_output(),A_input(),A_timerinterrupt(),A_init(),B_input(), and B_init() which together will implement a stop-and-wait (i.e., the alternating bit protocol, which we referred to as rdt3.0 on page 6-13 of the class notes) unidirectional transfer of data from the A-side to the B-side. Your protocol should use both ACK and NACK messages..

You should choose a very large value for the average time between messages from sender's layer5, so that your sender is never called while it still has an outstanding, unacknowledged message it is trying to send to the receiver. I'd suggest you choose a value of 1000. You should also perform a check in your sender to make sure that when A_output() is called, there is no message currently in transit. If there is, you can simply ignore (drop) the data being passed to the A_output() routine.

You should put your procedures in a file called prog2.c. You will need the initial version of this file, containing my emulation routines, and the stubs for your procedures. Students should copy the version of prog2.c from the class web site.

This assignment can be completed on any machine supporting C. It makes no use of UNIX features. (You can simply grab the file from the class sites and copy the file to whatever machine and OS you choose).

As always, you should hand in a code listing, a design document (as described in the handout accompanying the first programming assignment), and sample output.

For your sample output, your procedures should print out a message whenever an event occurs at your sender or receiver (a message/packet arrival, or a timer interrupt) as well as any action taken in response. You should hand in output for a run up to the point (approximately) when 10 messages have been ACK'ed correctly at the receiver, a loss probability of 0.1, and a corruption probability of 0.3, and a trace level of 2. You should annotate your printout with a colored pen showing how your protocol correctly recovered from packet loss and corruption.

Make sure you read the ``helpful hints'' for this assignment following the description of the extra credit assignment.

Extra Credit Assignment

You are to write the procedures, A_output(),A_input(),A_timerinterrupt(),A_init(),B_input(), and B_init() which together will implement a Go-Back-N unidirectional transfer of data from the A-side to the B-side, with a window size of 8. Your protocol should use both ACK and NACK messages. Consult the basic assignment above for information about how to obtain the network emulator.

I would STRONGLY recommend that you first implement the basic assignment (Alternating Bit) and then extend your code to implement the extra-credit assignment (Go-Back-N). Believe me - it will not be time wasted! However, some new considerations for your Go-Back-N code (which do not apply to the Alternating Bit protocol) are:

A_output(message),
where message is a structure of type msg, containing data to be sent to the B-side.

Your A_output() routine will now sometimes be called when there are outstanding, unacknowledged messages in the medium - implying that you will have to buffer multiple messages in your sender. Also, you'll also need buffering in your sender because of the nature of Go-Back-N: sometimes your sender will be called but it won't be able to send the new message because the new message falls outside of the window.

Rather than have you worry about buffering an arbitrary number of messages, it will be OK for you to have some finite, maximum number of buffers available at your sender (say for 50 messages) and have your sender simply abort (give up and exit) should all 50 buffers be in use at one point (Note: using the values I have given you below, this should never happen!) In the ``real-world,'' of course, one would have to come up with a more elegant solution to the finite buffer problem!
 

A_timerinterrupt()
This routine will be called when A's timer expires (thus generating a timer interrupt). Remember that you've only got one timer, and may have many outstanding, unacknowledged packets in the medium, so you'll have to think a bit about how to use this single timer.


Consult the basic assignment above for a general description of what to hand in. You should hand in output for a run that was long enough so that at least 20 messages were successfully transfered from sender to receiver (i.e., the sender receives ACK for these messages) transfers, a loss probability of 0.2, and a corruption probability of 0.2, and a trace level of 2, and a mean time between arrivals of 10. You should annotate parts of your printout with a colored pen showing how your protocol correctly recovered from packet loss and corruption.
 

Helpful Hints and the like


Here are the class files and source code for the JAVA files that you'll need if you want to do the assignment in Java rather than C.
Some notes: