// 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); } }