You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
12 KiB
Java

// 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
<br/> 1.4 (9th March 2006, KJT): updated for JDK 1.5
<br/> 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<ProtocolEvent> 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<String> getServices() {
Vector<String> list = new Vector<String>();
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<ProtocolEvent>();
state = CLOSED;
}
public boolean hasTimer(String type) {
return(type.equals("DT") || type.equals("DR") || type.equals("CR"));
}
public Vector<ProtocolEvent> performService(String s) {
Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
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<ProtocolEvent> receivePDU(PDU pdu) {
Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
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());
}
}