// ABRAProtocol.java package protocol; import java.util.*; import support.*; /** This is the class for the Abracadabra protocol sender. @author Iain A. Robin, Kenneth J. Turner @version 1.0 (1st September 1999, IAR): initial version
1.4 (9th March 2006, KJT): updated for JDK 1.5
1.5 (27th July 2010, KJT): minor tidying */ public class ABRAProtocol implements ProtocolEntity, Timeouts { // Protocol state constants private static final int CLOSED = 0; // waiting for connection request private static final int CR_SENT = 1; // waiting for connection confirmation private static final int CR_RECV = 2; // waiting for ConResp from user private static final int SEND = 3; // prepared to send data PDU private static final int WAIT = 4; // waiting for acknowledgement of data private static final int DR_SENT = 5; // waiting for disconnection confirm // Protocol data unit types public static final String CR = "Connection Request"; public static final String CC = "Connection Confirmation"; public static final String DT = "Data Transfer"; public static final String AK = "Acknowledgement"; public static final String DR = "Disconnection Request"; public static final String DC = "Disconnection Confirmation"; public static final int maxRetries = 3; private PDU pduSent; private PDU lastDTpdu; private int sendSeq; private int recvSeq; private ProtocolEntity peer; private ABRAService user; private Vector entityEvents; // events from entity private Medium medium; private String name; private boolean timerEnabled; private int crRemaining; private int drRemaining; private int dtRemaining; private boolean dtOrAk; private int state; public ABRAProtocol(Medium m, String name) { this.name = name; medium = m; initialise(); } public String getName() { return(name); } public Vector getServices() { Vector list = new Vector(); switch (state) { case CR_SENT: // waiting for connection confirmation if (timerEnabled) { if (crRemaining > 1) list.addElement("Timeout - resend " + CR); else list.addElement("Retry limit reached - send " + DR); } break; case WAIT: // waiting for acknowledgement of data if (timerEnabled) { if (dtRemaining > 1) list.addElement("Timeout - resend DT(" + lastDTpdu.seq + ")" ); else list.addElement("Retry limit reached - send " + DR); } break; case DR_SENT: // waiting for disconnection confirmation if (timerEnabled) { if (drRemaining > 1) { list.addElement("Timeout - resend " + DR); } else { // Retry limit reached dtRemaining--; state = CLOSED; } } break; } return(list); } /** Increment PDU sequence number (alternating bit) */ private int inc(int seq) { return(1 - seq); } public void initialise() { entityEvents = new Vector(); state = CLOSED; } public boolean hasTimer(String type) { return(type.equals("DT") || type.equals("DR") || type.equals("CR")); } public Vector performService(String s) { Vector events = new Vector(); switch (state) { case CR_SENT: if (s.startsWith("Timeout")) { crRemaining--; transmitPDU(new PDU("CR"), peer); // resend connection request PDU events.addElement( new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); } if (s.startsWith("Retry limit")) { crRemaining--; transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DR"), peer); // send disconnection request PDU events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } break; case WAIT: if (s.startsWith("Timeout")) { dtRemaining--; transmitPDU(lastDTpdu, peer); events.addElement( new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); } if (s.startsWith("Retry limit")) { transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); dtRemaining--; transmitPDU(new PDU("DR"), peer); // send disconnection request PDU events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } break; case DR_SENT: if (s.startsWith("Timeout")) { drRemaining--; transmitPDU(new PDU("DR"), peer); events.addElement( new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent)); } break; } for (Enumeration e = entityEvents.elements(); e.hasMoreElements(); ) events.addElement((ProtocolEvent) e.nextElement()); return(events); } public Vector receivePDU(PDU pdu) { Vector events = new Vector(); String pduType = ""; if (pdu != null) pduType = pdu.type; switch (state) { case CLOSED: if (pduType.equals("ConReq")) { // ConReq primitive? transmitPDU(new PDU("CR"), peer); // send connection request PDU dtOrAk = false; sendSeq = 0; recvSeq = 0; crRemaining = maxRetries; state = CR_SENT; events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); } else if (pduType.equals("CR")) { // connection request PDU? transmitPDU(new PDU("ConInd"), user); events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); dtOrAk = false; sendSeq = 0; recvSeq = 0; state = CR_RECV; } else if (pduType.equals("DR")) { // got disconnect request PDU transmitPDU(new PDU("DC"), peer); // send disconnect confirm PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); } break; case CR_SENT: if (pduType.equals("CC") || // connection confirmation PDU? pduType.equals("CR")) { // connection request PDU? // (both stations have requested connections at the same time) transmitPDU(new PDU("ConConf"), user); events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); timerEnabled = false; state = SEND; } else if (pduType.equals("DR")) { // disconnection request PDU? transmitPDU(new PDU("DisInd"), user); events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); timerEnabled = false; state = CLOSED; } else if (pduType.equals("DisReq")) { // disconnection request prim? // transmitPDU(new PDU("DisInd"), user); // events.addElement( // new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DR"), peer); // send disconnect request PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } break; case CR_RECV: if (pduType.equals("ConResp")) { transmitPDU(new PDU("CC"), peer); // send connection confirm PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); state = SEND; } else if (pduType.equals("DR")) { transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DC"), peer); // send disconnection confirm PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); state = CLOSED; } else if (pduType.equals("ConReq")) { transmitPDU(new PDU("ConConf"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("CC"), peer); // send connection confirm PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); state = SEND; } else if (pduType.equals("DisReq")) { transmitPDU(new PDU("DR"), peer); // send disconnect request PDU events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); state = CLOSED; } break; case WAIT: if (pduType.equals("AK")) { if (pdu.seq == sendSeq) { dtOrAk = true; timerEnabled = false; user.setOKToSend(true); state = SEND; } else { // wrong AK : enter error phase transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DR"), peer); // send disconnect request PDU events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } } // break intentionally omitted case SEND: if (state == SEND // avoid drop-through from WAIT && pduType.equals("DatReq")) { // DatReq received from user transmitPDU(new PDU("DT", sendSeq, pdu.getSDU()), peer); events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); user.setOKToSend(false); sendSeq = inc(sendSeq); dtRemaining = maxRetries; state = WAIT; } else if (pduType.equals("DR")) { // disconnection request PDU? transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DC"), peer); events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); timerEnabled = false; state = CLOSED; } else if (pduType.equals("DT")) { // data PDU received dtOrAk = true; if (pdu.seq == recvSeq) { // check sequence number correct recvSeq = inc(recvSeq); transmitPDU(new PDU("DatInd", pdu.getSDU()), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); } transmitPDU(new PDU("AK", recvSeq), peer); events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); // no state transition } if (pduType.equals("CC") || pduType.equals("DC")) { transmitPDU(new PDU("DisInd"), user); events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DR"), peer); events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } if (pduType.equals("CR")) { // connection request PDU? if (!dtOrAk) { transmitPDU(new PDU("CC"), peer); events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); // no state transition } else { transmitPDU(new PDU("DisInd"), user); events.addElement( new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); transmitPDU(new PDU("DR"), peer); events.addElement( new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } } if (pduType.equals("DisReq")) { transmitPDU(new PDU("DR"), peer); events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent)); drRemaining = maxRetries; state = DR_SENT; } break; case DR_SENT: // awaiting disconnect confirm if (pduType.equals("DC") || // disconnect confirm PDU? pduType.equals("DR")) { // disconnect request PDU? // both stations have requested disconnection at the same time timerEnabled = false; state = CLOSED; } if (pduType.equals("ConReq")) { transmitPDU(new PDU("DisInd"), user); events.addElement(new ProtocolEvent(ProtocolEvent.DELIVER, pduSent)); } if (timerEnabled && drRemaining <= 1) { drRemaining--; state = CLOSED; } break; } return(events); } public void setPeer(ProtocolEntity peer) { this.peer = 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) { timerEnabled = enabled; } public void setUser(ProtocolEntity user) { this.user = (ABRAService)user; } public void transmitPDU(PDU pdu, ProtocolEntity destination) { pdu.setSource(this); pdu.setDestination(destination); if (destination == peer) entityEvents = medium.receivePDU(pdu); else entityEvents = user.receivePDU(pdu); pduSent = pdu; if (pdu.type.equals("DT")) lastDTpdu = pdu; for (Enumeration e = entityEvents.elements(); e.hasMoreElements(); ) System.err.println("ABRAP transmitPDU: " + (ProtocolEvent) e.nextElement()); } }