Program #6 Solution

Program Description and Requirement
You will be implementing an RDT protocol on top of UDP. You will also make an Application that will have a Client download an EXE file from a Server and then execute that file. The Client and Server will both be console programs.
There will be 4 major classes, one per file:
(1) RDTServer class—waits for a Client to connect; once contacted, grab a random EXE file and send it the Client. After the exchange takes place, the Server shuts down.
 First, it sends the file name, NOT null-terminated (MSG_FILE_NAME).
 Next, it sends the file content (MSG_FILE_DATA), in 256-byte chunks, except the last chunk.
 Finally, it sends the MSG_FILE_DONE message.
public class RDTServer
{
public static int progNumToSend = 1;
private RDT3 rdt;
private FileInputStream inFile;
private String filename;
public RDTServer(String ipAddress, int receivePort, int peerReceivePort)
throws Exception
{
rdt = new RDT3(ipAddress, receivePort, peerReceivePort);
Thread.sleep(100);
}
/**
This is the run method
*/
public void run() throws Exception
{
try
{
System.out.println("Started the Server.");
getRequest();
filename = App.getRandomExeFile();
if ( filename == null )
{
...
}
else
{
sendFileName();
...
}
}
catch (Exception e)
{
...
}
finally
{
//shut down the Server properly
}
} //run()
/**
This method uses a while loop to send data chunks by calling rdt.sendData().
*/
private void sendFile() throws Exception
{
...
}

/**
This method sends the file name by calling rdt.sendData().
*/
private void sendFileName()
{
...
}
/**
This method uses a while loop and waits until a request has been received by
calling rdt.receiveData().
*/
private void getRequest()
{
...
}
/**
The main() instantiates an instance of RDTServer.
*/
public static void main(String[] args)
{
...
}
}
The Server uses getRandomExeFile() provided in App class to get the filename of a file to download. To open the file, you need the path (EXE_SUBFOLDER ) prepended, such as: inFile = new FileInputStream(App.EXE_SUBFOLDER + fileName);
Shut down the Server as follows, this also shuts down all their Threads.
Thread.sleep(2000); // Give protocols time to finish
System.out.println("Shutting down.");
System.exit(0);
(2) RDTClient class—contacts the Server, downloads an EXE file, executes the EXE file, and shuts down the
same way as the Server (see the codes above). To run the EXE in the Client after it has been received, do the
following: Runtime.getRuntime().exec(fileName);
public class RDTClient
{
private RDT3 rdt;
private String filename;
private FileOutputStream outFile;
public RDTClient(String ipAddress, int receivePort, int peerReceivePort)
throws Exception
{
rdt = new RDT3(ipAddress, receivePort, peerReceivePort);
Thread.sleep(100);
}
// Client run method
public void run() throws Exception
{
...
sendFileRequest();
if ( getFilename() )
{
//new a FileOutputStream and continuously call rdt.receiveDate() to get file chunks.
//run the EXE file when it is well-received.
}
}
catch (Exception e)
{
...
}
finally
{
//shut down the Client properly.
}
}

/**
This method sends a request by calling rdt.sendData().
*/
private void sendFileRequest()
{
...
}
/**
This methods loops and waits for the filename sent from the Server by calling
rdt.receiveData()
*/
private boolean getFilename()
{
...
}
/**
The main method instantiates an instance of Client.
*/
public static void main(String[] args)
{
...
}
}
(3) App class—has common data and routines for the Client and Server. An application message consists of a one-byte type followed by the data. The types are as follows.
 MSG_REQUEST_EXE_FILE = 1
Sent from the Client, has no data
Starts the session - says that Client wants an EXE.
 MSG_FILE_NAME = 2
Sent from the Server, has file name as the data - NOT null-terminated.
The Client will make an output file using this name. The data from MSG_FILE_DATA segments will be written to this file.
 MSG_FILE_DATA = 3
Sent from the Server, has 1 to 256 bytes of file data
 MSG_FILE_DONE = 4
Sent from the Server, has no data.
Indicates the last piece of file data was sent.
Server shuts down. Client executes the received file and shuts down.
 MSG_NO_EXE_FILE_AVAILABLE = 5
Indicates the Server had no EXE files in the folder.
Both sides will print appropriate messages and shut down.
Use the codes below:
public class App
{
// Application Message Types
public static final byte MSG_REQUEST_EXE_FILE = 1;
public static final byte MSG_FILE_NAME = 2;
public static final byte MSG_FILE_DATA = 3;
public static final byte MSG_FILE_DONE = 4;
public static final byte MSG_NO_EXE_FILE_AVAILABLE = 5;
// Application message maximum sizes
public static final int MAX_DATA_SIZE = 256;
public static final int MAX_MSG_SIZE = MAX_DATA_SIZE + 1;
// Recevier port numbers
public static int CLIENT_RCV_PORT_NUM = 5281;
public static int CLIENT_PEER_RCV_PORT_NUM = 7281;
public static int SERVER_RCV_PORT_NUM = 7281;
public static int SERVER_PEER_RCV_PORT_NUM = 5281;
// Exe programs subfolder assuming the subfolder name is Progs
public static String EXE_SUBFOLDER = ".\\Progs\\";

/**
* Returns a random EXE file name from EXE_SUBFOLDER.
* If there are no EXE files in Progs, null is returned.
* The file name doesn't have the path.
* @return a random EXE filename or null.
*/
public static String getRandomExeFile()
{
java.io.File dir = new java.io.File(EXE_SUBFOLDER);
String [] entireFileList = dir.list();
String [] exeFileList = new String[entireFileList.length];
int exeCount = 0;
for( int i = 0; i < entireFileList.length; i++ )
{
if( entireFileList[i].toLowerCase().endsWith(".exe") )
{
exeFileList[exeCount++] = entireFileList[i];
}
}
if(exeCount == 0)
return null;
int index = new java.util.Random().nextInt(exeCount);
return exeFileList[index];
}
}
(4) RDT3 class—implements the Reliable Data Transfer protocol on top of UDP using the basics of the RDT 3.0
from the textbook with some minor modifications. In addition, we will build in some "error injection" to test robustness. This class will have Sender and Receiver tasks that are based on the state machines from the lecture. These will be embedded classes in RDT3 so they can access the state variables in RDT3. This class CANNOT access App or RDTClient or RDTServer class. Both the Client and Server will use this exact same RDT3 class. This class should not care what application usdes it.
Whenever an error is injected, a message starting with ???? is printed out. When a bad packet for the Receiver is received or a bad ACK for the Sender is received, then a message starting with !!!! is printed. You will print lots of other messages to help tracking what is going on, but ONLY these messages can start with ? or !.
???? Checksum altered for Testing Purposes.
???? ACK dropped for Testing Purposes.
!!!! Rcv data bad - resend prev ACK.
!!!! BAD ACK received.
Below are required pieces for this class. You need to comment and fill in. You CANNOT add or remove any ???? or !!!! messages. The "peer" is the other host/end system. The sender of this side "communicates"
with the peer receiver and vice versa. The App Layer calls sendData if it wants to send data to its peer. The data gets sent from this Sender to the peer's Receiver. The App Layer calls receiveData to get data sent from peer Sender and received by this Receiver. A RDT3 segment has a one byte header followed by the APP data
followed by a one byte checksum: Header | App Data | Check sum
The one-byte header has two relevant bits: Bit 0: Sequence number, either 0 or 1
Bit 1: ACK bit, 1 = ACK, 0 = App Data segment
The checksum is the bitwise XOR of the Header and App Data and is appended as the last byte of the segment. When the receiver gets the segment, it performs the checksum on the entire segment and should
get 0!
You CANNOT add any other state variables besides those listed below for each class. You can (and should) add more methods; however, the methods I give you must be used as is and can't be modified except to comment them more. Recall that a datagram has the length in it: receivePacket.getLength()

public class RDT3
{
static private final int SEQ_MASK = 1; // 2 to the 0th power - bit 0
static private final int ACK_MASK = 2; // 2 to the 1st power - bit 1
private InetAddress peerIpAddress;
private int rcvPortNum;
private int peerRcvPortNum;
private boolean dataWaitingToBeSent = false;
private boolean dataWasReceived = false;
private byte[] dataToSend;
private byte[] dataReceived;
public RDT3( String peerIP, int _rcvPortNum, int _peerRcvPortNum )
throws java.net.UnknownHostException
{
rcvPortNum = _rcvPortNum;
peerRcvPortNum = _peerRcvPortNum;
peerIpAddress = InetAddress.getByName( peerIP );
new Sender().start();
new Receiver().start();
}
public void sendData( byte[] data )
{
while ( dataWaitingToBeSent )
{
Thread.yield(); // Wait until it's ready for the next packet
}
dataToSend = data;
dataWaitingToBeSent = true;
}
public byte[] receiveData()
{
// You write this one, similar to sendData.
// Uses dataWasReceived and dataReceived.
}
private byte checkSum( byte[] data, int len )
{
byte cs = 0;
// You write the middle - loop through and XOR all the bytes
// into cs. The XOR operator is ^
if( oneInN_Occurs(20) )
{
System.out.println( "???? Checksum altered for Testing Purposes." );
cs = (byte)(cs + 1); // Bad Checksum - error to test RDT robustness
}
return cs;
}
// Random - true with probability 1 in N
private boolean oneInN_Occurs( int n )
{
if ( n <= 1 )
{
return true;
}
return new java.util.Random().nextInt( n ) == 0;
}
private class Receiver extends Thread
{
private DatagramSocket receiverSocket;
private byte[] receiveData = new byte[2 * App.MAX_DATA_SIZE];
private DatagramPacket receivePacket;
private int expectedSeqNum = 0;
@Override
CS3830 Fall 2014 Dr. Lily Chang
6 | P a g e
public void run()
{
try
{
receiverSocket = new DatagramSocket( rcvPortNum );
System.out.println( "Started Receiver." );
}
catch ( Exception ex )
{
System.out.println( "Can't start Receiver: " + ex.toString() );
System.exit( 1 );
}
while ( true )
{
try
{
// Implement the RDT 2.2 Receiver - make some methods.
// Data "received" is good if:
// checksum is 0, seq # is expected, ACK bit is 0
// If data received is not good, output the message:
// "!!!! Rcv data bad - resend prev ACK"
// A slight change from RDT 2.2:
// Don't send the ACK until after the App layer retrieves
// the data. So when a "good" packet is received, do:
// dataWasReceived = true;
// while ( dataWasReceived )
// Thread.yield();
// This will sit and wait until the APP layer gets the data,
// after which you will send the ACK.
// You need to understand why!
}
catch ( Exception ex )
{
System.out.println( "Error in Recv loop: " + ex.toString() );
}
}
}
// I'll give you this one - it has the error injection in it
private void sendAck( int ackNum )
{
if ( oneInN_Occurs( 10 ) )
{
System.out.println( "???? ACK dropped for Testing Purposes." );
return; // Lose ACK - inject erros to test RDT robustness
}
try
{
final int dataLen = 2;
byte[] data = new byte[dataLen];
data[0] = (byte) (ackNum + ACK_MASK);
data[1] = data[0]; // 2 bytes = Checksum is the same as byte[0]
// You finish - get port from receivePacket
}
catch ( Exception ex )
{
System.out.println( "Rcv - Error sending ACK: " + ex.toString() );
}
}
} // end Receiver class
private class Sender extends Thread
{
private DatagramSocket senderSocket;
private byte[] rcvData = new byte[4]; // Only gets ACKs back
private DatagramPacket rcvPacket;
private int curSeqNum = 0;
CS3830 Fall 2014 Dr. Lily Chang
7 | P a g e
@Override
public void run()
{
// Make a senderSocket similar to how receiverSocket was
// made in Receiver, except the DatagramSocket won't
// take a port number parameter.
while ( true )
{
try
{
// Implement the RDT 3.0 Sender - make some methods.
// If the ACK received is bad, then output the message:
// "!!!! BAD ACK received."
// Technically, if it is bad, you don't know if it is an
// ACK or not, but the Sender is only supposed to receive ACKs.
// One small change from RDT 3.0: restart the timer on bad ACK.
// For the "timer", set socket timeout before the receive call as:
// senderSocket.setSoTimeout( 500 );
// If it throws the exception, it has timed out and you
// need to resend the packet.
// Note: Datagrams are sent to (peerIpAddress, peerRcvPortNum)
}
catch ( Exception ex )
{
System.out.println( "Error in Sender loop: " + ex.toString() );
}
}
}
// I'll write part of this - it has error injection.
private void sendPacket()
{
try
{
int dataLen = 2 + dataToSend.length;
byte[] data = new byte[dataLen];
data[0] = (byte) curSeqNum;
// You finish it - copy data and append checksum (call checkSum).
System.out.println( "Sender sent data pkt: " + curSeqNum );
}
catch ( Exception ex )
{
System.out.println("Sender send packet failed: " + ex.toString());
}
}
} // end Sender class
} // end RDT3 class
The Client and Server are both "one-shot", not infinite loops. If there is not an EXE file on the Server, then the MSG_NO_EXE_FILE_AVAILABLE is sent and both sides shut down. The Client and Server MUST use RDT3 for all sending and receiving (sendData, receiveData). They CANNOT have DatagramSocket, DatagramPacket, or anything related to these anywhere. Use Thread.yield() when you are looping waiting for a variable to change.
Additional Notes
1. You MUST follow the software development ground rules from CS2430 (posted on D2L.) Name all your constants (except for the constants I gave you.)
Powered by