// TFTPSender.java (C) K. J. Turner, K. A. Whyte 05/06/01 // Trivial File Transfer Protocol sender (client) package protocol; // protocol package import java.util.Vector; // vector (list) import support.*; // protocol entity support /** This is the class for a Trivial File Transfer Protocol sender. @author Kenneth J. Turner, Kenneth A. Whyte @version 1.0 (5th June 2001, KAW): initial version
1.4 (9th March 2006, KJT): updated for JDK 1.5
1.5 (27th July 2010, KJT): unchecked warning suppressed */ public class TFTPSender // protocol sender (client) implements ProtocolEntity, Timeouts { // protocol entity, timeout // simulator variables private ProtocolEntity peer; // peer entity (server) private Medium medium; // communications medium private String name; // entity name // protocol variables private int pduNo; // PDU seq number sent/expected private PDU pduReceived; // PDU received private PDU pduSent; // PDU sent private boolean timerEnabled; // whether timer is enabled private float errorProb = 0.8f; // file I/O error probability private int fileNo; // file number to transfer // protocol state int state; // current protocol state final static int idle = 0; // no connection final static int readRequest = 1; // file to be read final static int reading = 2; // reading data final static int writeRequest = 3; // file to be written final static int writing = 4; // writing data final static int waitLast = 5; // wait for last re-send // protocol messages final static String ack = "ACK"; // acknowledgement message type final static String data = "DATA"; // data message type final static String error = "ERROR"; // error message type final static String rrq = "RRQ"; // read request message type final static String wrq = "WRQ"; // write request messagetype // protocol methods public TFTPSender(Medium m, String name) { // construct sender instance this.name = name; // set protocol entity name medium = m; // set underlying medium initialise (); // initialise protocol } public String getName () { // get protocol entity name return((name)); // return protocol entity name } public Vector getServices() { // get protocol entity services Vector events = // list of events new Vector(); String pduType; // received PDU type int pduSeq; // received PDU seq number if (pduReceived != null) { // PDU received? pduType = pduReceived.type; // get received PDU type pduSeq = pduReceived.seq; // get received PDU seq number if ((state == readRequest || // read request or ... state == reading || // reading or ... state == waitLast) && // awaiting re-send, and ... pduType.equals(data)) { // data message? if (pduSeq == pduNo) { // expected sequence number? timerEnabled = false; // cancel timeout events.addElement( // send acknowledgement ack + "(" + pduNo + ") - send acknowledgement"); } else if (state == readRequest) // wrong sequence, read req? events.addElement( // report seq number error error +"(wrong data sequence number) - report error"); } else if ((state == writeRequest || // write request or state == writing) && // writing, and ... pduType.equals(ack)) { // write and acknowledgement? if (pduSeq == pduNo) { // expected sequence number? timerEnabled = false; // cancel timeout if (pduSent != null && // valid previous PDU and ... pduSent.sdu.equals("Dlast")) // last data sent? reinitialise(); // re-initialise protocol else { // more data to send events.addElement( // send data data + "(" + (pduNo + 1) + ",D" + pduNo + ") - send data"); events.addElement( // send last data data + "(" + (pduNo + 1) + ",Dlast) - send last data"); } } else if (state == writing && // writing and ... pduSeq == pduNo - 1) // repeated acknowledgement? events.addElement( // send data data + "(" + pduSent.seq + "," + pduSent.sdu + ") - re-send data"); else if (state == writeRequest) // wrong sequence, write req? events.addElement( // report seq number error error +"(wrong acknowledgement sequence number) - report error"); } else if (pduType.equals(error)) { // error message? state = idle; // back to not connected } } if (state == idle) { // not connected? events.addElement( // read file rrq + "(file" + fileNo + ") - ask to read file"); events.addElement( // write file wrq + "(file" + fileNo +") - ask to write file"); } if (state == waitLast) // waiting for last re-send? events.addElement( // add close event "Close - presume all retransmissions over"); if (state == writing && // writing and ... pduSent != null && // PDU was sent and ... !pduSent.sdu.equals("Dlast") && // PDU was not last and ... TFTPMedium.random() < errorProb) // file I/O error occurs? events.addElement( // report I/O error error +"(data for write unavailable) - report error"); if (timerEnabled) // timer is enabled? events.addElement( // add timeout event "Timeout - presume loss of message and resend"); return(events); // return list of events } public boolean hasTimer(String type) { // PDU needs timer? return((true)); // report it does } public void initialise () { // initialise protocol fileNo = 0; // initialise file number reinitialise(); // re-initialise protocol } public Vector performService (String s) { // protocol service Vector events = // initialise events list new Vector(); int start, middle, end; // start/middle/end subscripts int pduSeq; // PDU sequence number String pduData; // PDU data if (s.startsWith (rrq)) { // send read request? start = s.indexOf ('(') + 1; // get filename start end = s.indexOf (')'); // get filename finish pduData = s.substring(start, end); // get filename transmitPDU( // send read request new TFTPMessage(rrq , pduData), peer); pduNo = 1; // expect seq number 1 state = readRequest; // now requested write } else if (s.startsWith (ack)) { // send acknowledgement? start = s.indexOf ('(') + 1; // get seq number start end = s.indexOf (')'); // get seq number end pduSeq = // get sequence number Integer.parseInt(s.substring(start, end)); if (pduReceived != null && // valid PDU received and ... pduReceived.sdu.equals("Dlast")) // last data? state = waitLast; // wait for last re-send else { // still reading pduNo++; // to next sequence number state = reading; // (now) reading } transmitPDU( // send acknowledgement new TFTPMessage(ack, pduSeq), peer); } else if (s.startsWith (wrq)) { // send write request? start = s.indexOf ('(') + 1; // get filename start end = s.indexOf (')'); // get filename finish pduData = s.substring(start, end); // get filename transmitPDU( // send write request new TFTPMessage(wrq , pduData), peer); state = writeRequest; // now requested write } else if (s.startsWith (data)) { // send data? start = s.indexOf ('(') + 1; // get seq number start middle = s.indexOf (','); // get comma position end = s.indexOf (')'); // get seq number end pduSeq = // get sequence number Integer.parseInt(s.substring(start, middle)); pduData = // get data content s.substring(middle + 1, end); transmitPDU( // send data new TFTPMessage(data, pduSeq, pduData), peer); pduNo++; // to next sequence number state = writing; // (now) writing } else if (s.startsWith (error)) { // send error? start = s.indexOf ('(') + 1; // get error message start end = s.indexOf (')'); // get error message end pduData = s.substring(start, end); // get error message transmitPDU( // send error new TFTPMessage(error, pduData), peer); state = waitLast; // wait for last re-send } else if (s.startsWith ("Close")) { // close? events.addElement( // add closed comment new ProtocolEvent(ProtocolEvent.COMMENT, this, "Closed")); reinitialise(); // re-initialise protocol } if (s.startsWith ("Timeout")) { // timeout? transmitPDU(pduSent, peer); // re-send PDU events.addElement( // add timeout event and PDU new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); } else if (pduSent != null) // PDU to send? events.addElement( // transmit PDU new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); return(events); // return list of events } public Vector receivePDU(PDU pdu) { // handle received PDU pduReceived = pdu; // store PDU if (pduReceived.type.equals(error)) // error message? reinitialise(); // re-initialise protocol return((new Vector())); // return no events } public void reinitialise() { // re-initialise protocol state = idle; // initialise state pduNo = 0; // initialise seq number pduReceived = null; // initialise no PDU received pduSent = null; // initialise no PDU sent timerEnabled = false; // initialise no timeout fileNo++; // to next file number } public void setPeer(ProtocolEntity peer) { // set protocol peer this.peer = peer; // set this entity's peer } /** Set the timer for a specified PDU. @param pdu PDU @param enabled whether the timer is enabled */ public void setTimer(PDU pdu, boolean enabled) { // set timer status if (state != idle) // not idle? timerEnabled = enabled; // store timer status } public void transmitPDU( // transmit PDU PDU pdu, ProtocolEntity dest) { // for given PDU, destination pdu.setSource (this); // source is this entity pdu.setDestination(dest); // destination is as given pduSent = pdu; // copy PDU sent medium.receivePDU(pdu); // medium receives PDU pduReceived = null; // note no PDU in response timerEnabled = false; // note no timeout } }