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.
221 lines
6.5 KiB
Java
221 lines
6.5 KiB
Java
4 years ago
|
// 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
|
||
|
<br/> 1.4 (9th March 2006, KJT): updated for JDK 1.5
|
||
|
<br/> 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<String> services;
|
||
|
private boolean windowClosed;
|
||
|
private Vector<ProtocolEvent> 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<String> 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<String>();
|
||
|
windowClosed = false;
|
||
|
entityEvents = new Vector<ProtocolEvent>(); // 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<ProtocolEvent> performService(String s) {
|
||
|
int startIndex, endIndex;
|
||
|
String seqString;
|
||
|
Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
|
||
|
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<ProtocolEvent> receivePDU(PDU pdu) {
|
||
|
Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
}
|