// SWP3Sender.java
package protocol;
import java.util.Enumeration; // enumeration
import java.util.Vector; // vector (list)
import support.*; // protocol entity support
/**
This is the class for a three-column Sliding Window 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 SWP3Sender implements ProtocolEntity, Timeouts {
// SWP3 Sliding Window sender protocol entity
private static final String SEND = "Send";
private static final String RESEND = "Timeout - resend";
private static final String BLOCKED = "Blocked";
private static final String CLEAR = "Clear";
private int maxSeq; // maximum seq. no.
private int winSize; // transmit window
private int lowerWindowEdge; // ack seq. no.
private int upperWindowEdge; // next seq. no.
private boolean[] timerEnabled;
private Medium medium;
private ProtocolEntity peer;
private Vector services;
private boolean windowClosed;
private Vector entityEvents; // events from entity
public SWP3Sender(int maxSeq, int winSize, Medium m) {
setMaxSeq(maxSeq);
setWindowSize(winSize);
medium = m;
initialise();
}
private void addService(String s) {
if (services.indexOf(s) < 0)
services.addElement(s);
}
private ProtocolEvent comment(String c) {
return(new ProtocolEvent(ProtocolEvent.COMMENT, this, c));
}
public String getName() {
return("Sender");
}
public Vector getServices() {
int seq;
int first = -1;
int last = -1;
// remove any previous resend actions
for (Enumeration e = services.elements(); e.hasMoreElements(); ) {
String s = (String)e.nextElement();
if(s.startsWith(RESEND)) services.removeElement(s);
}
for (seq = lowerWindowEdge; isWithinWindow(seq); seq = inc(seq)) {
if (timerEnabled[seq]) {
if (first < 0) first = seq;
last = seq;
}
}
if (first >= 0) { // >=1 PDU may time out
// ensure there is no option to send new data until timeout dealt with
services.removeAllElements();
if (last == first) // 1 PDU may be resent
addService(RESEND + " DT(" + first + ")");
else // >1 PDU may be resent
addService(RESEND + " DT(" + first + ") - (" + last + ")");
}
if (!windowClosed) {
addService(SEND + " DT(" + seq + ")");
}
return(services);
}
public boolean hasTimer(String type) {
return(true);
}
private int inc(int seq) {
return(mod(seq + 1));
}
public void initialise() {
timerEnabled = new boolean[maxSeq + 1];
for (int i = 0; i <= maxSeq; i++) timerEnabled[i] = false;
lowerWindowEdge = 0;
upperWindowEdge = 0;
services = new Vector();
windowClosed = false;
entityEvents = new Vector(); // empty medium events
}
private boolean isWithinWindow(int seq) {
return (lowerWindowEdge <= seq && seq < upperWindowEdge) ||
(upperWindowEdge < lowerWindowEdge &&
(seq >= lowerWindowEdge || seq < upperWindowEdge));
}
private int mod(int n) {
int base = maxSeq + 1;
n %= base;
return(n >= 0 ? n : n + base);
}
public Vector performService(String s) {
int startIndex, endIndex;
String seqString;
Vector events = new Vector();
if (s.startsWith(SEND)) {
PDU pdu = new PDU("DT", upperWindowEdge);
transmitPDU(pdu, peer);
events.addElement(new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu));
removeService(s);
upperWindowEdge = inc(upperWindowEdge);
windowClosed = mod(upperWindowEdge - lowerWindowEdge) >= winSize;
if (windowClosed)
events.addElement(comment(BLOCKED));
}
if (s.startsWith(RESEND)) {
// get sequence number of first data PDU to be resent
startIndex = s.indexOf( '(' ) + 1;
endIndex = s.indexOf( ')' );
seqString = s.substring(startIndex, endIndex);
int seq1 = Integer.parseInt(seqString);
// get sequence number of last data PDU to be resent
startIndex = s.lastIndexOf( '(' ) + 1;
endIndex = s.lastIndexOf( ')' );
seqString = s.substring(startIndex, endIndex);
int seq2 = Integer.parseInt(seqString);
releaseTimers(seq1, inc(seq2));
// retransmit all unacknowledged data PDUs already sent and timed out
for (int p = seq1; p != inc(seq2); p = inc(p)) {
PDU pdu = new PDU("DT", p);
transmitPDU(pdu, peer);
events.addElement(new ProtocolEvent(ProtocolEvent.TIMEOUT, pdu));
}
removeService(s);
}
for (Enumeration e = entityEvents.elements(); // get medium events
e.hasMoreElements(); )
events.addElement( // add medium event
(ProtocolEvent) e.nextElement());
return(events);
}
private void releaseTimers(int lower, int upper) {
for (int seq = lower; seq != upper; seq = inc(seq)) {
timerEnabled[seq] = false;
}
}
private void removeService(String s) {
if (services.isEmpty())
return;
for (Enumeration e = services.elements(); e.hasMoreElements(); ) {
String s1 = (String)e.nextElement();
if (s1.equals(s)) {
services.removeElement(s1);
break;
}
}
}
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[pdu.seq] = enabled;
}
public Vector receivePDU(PDU pdu) {
Vector events = new Vector();
if (pdu != null && pdu.type.equals("AK")) { // respond to AK
int seq = pdu.seq;
if (isWithinWindow(mod(seq - 1))) {
releaseTimers(lowerWindowEdge, seq);
lowerWindowEdge = seq; // update sender window
if (windowClosed) {
// send window was full, indicate it is now clear:
events.addElement(comment(CLEAR));
}
windowClosed = false;
}
}
return(events);
}
public void setMaxSeq(int maxSeq) {
this.maxSeq = maxSeq;
}
public void setWindowSize(int winSize) {
this.winSize = winSize;
}
public void transmitPDU(PDU pdu, ProtocolEntity dest) {
pdu.setSource(this);
pdu.setDestination(dest);
entityEvents = medium.receivePDU(pdu);
}
}