// TFTPReceiver.java (C) K. J. Turner, K. A. Whyte 05/06/01 // Trivial File Transfer Protocol receiver (server) 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 TFTPReceiver // protocol receiver (server) implements ProtocolEntity, Timeouts { // protocol entity, timeout // simulator variables private ProtocolEntity peer; // peer entity (client) private Medium medium; // communications medium private String name; // entity name // protocol variables private int pduNo; // PDU seq number sent/expected private PDU pduSent; // PDU sent private PDU pduReceived; // PDU received private boolean timerEnabled; // whether timer is enabled private float errorProb = 0.8f; // file I/O error probability // protocol state int state; // current protocol state final static int idle = 0; // no connection final static int reading = 2; // reading data 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 TFTPReceiver(Medium m, String name) { // construct receiver 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 String pduData; // PDU data if (pduReceived != null) { // PDU received? pduType = pduReceived.type; // get received PDU type if (pduType.equals(rrq) || // read request or ... pduType.equals(wrq)) // write request? pduSeq = 0; // treat as sequence number 0 else pduSeq = pduReceived.seq; // get received PDU seq number pduData = pduReceived.sdu; // get received PDU data if (((state == idle || // idle or ... state == reading) && // reading, and ... pduType.equals(rrq)) || // read request, or ... ((state == reading || // reading or ... state == waitLast) && // awaiting re-send, and ... pduType.equals(ack))) { // acknowledgement? if (pduSeq == pduNo) { // expected acknowledgement? timerEnabled = false; // cancel timeout if (pduSent != null && // valid previous PDU and ... pduSent.sdu.equals("Dlast")) // last data sent? initialise (); // re-initialise protocol else { // more data to send events.addElement( // send data data + "(" + (pduNo + 1) + ",D" + (pduNo + 1) + ") - send data"); events.addElement( // send last data data + "(" + (pduNo + 1) + ",Dlast) - send last data"); } } else if (pduSeq == pduNo - 1) // repeated acknowledgement? events.addElement( // send data data + "(" + pduSent.seq + "," + pduSent.sdu + ") - re-send data"); } else if (((state == idle || // idle or ... state == writing) && // writing, and ... pduType.equals(wrq)) || // write request, or ... (state == writing && // writing and ... pduType.equals(data))) { // data? if (pduSeq == pduNo || // expected data or ... pduSeq == pduNo - 1) { // repeated data? timerEnabled = false; // cancel timeout events.addElement( // send acknowledgement ack + "(" + pduSeq + ") - send acknowledgement"); } } } if (state == waitLast) // waiting for last re-send? events.addElement( // add close event "Close - presume all retransmissions over"); if (state == reading && // reading and ... pduSent != null && // PDU was sent and ... !pduSent.sdu.equals("Dlast") && // PDU was not last and ... TFTPMedium.random() < errorProb) // file read error? events.addElement( // report reading error error +"(data for read 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) { // protocol uses timer? return((true)); // report it does } public void initialise () { // 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 } public Vector performService (String s) { // protocol service Vector events = // initialise events list new Vector(); int start, middle, end; // start/middle/end positions int pduSeq; // PDU sequence number String pduData; // PDU data 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 = pduSeq + 1; // update sequence number state = writing; // (now) writing } transmitPDU( // send acknowledgement new TFTPMessage(ack, pduSeq), peer); } 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 = s.substring(middle + 1, end); // get data contents transmitPDU( // send data new TFTPMessage(data, pduSeq, pduData), peer); pduNo = pduSeq; // update sequence number state = reading; // (now) reading } 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")); initialise (); // 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? initialise(); // re-initialise protocol return((new Vector())); // return no events } 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 } }