package reliability; import debug.*; import timer.*; import network.*; import java.io.*; import java.util.*; /***************************************************************** Implementation of a sliding-window sender *****************************************************************/ public class SlidingWindowSender extends TimerClient implements ReliableSender, PacketReceiver { /************************************************************ Create a new stop-and-wait sender. @param network the network to communicate on @param address the address of this host ************************************************************/ public SlidingWindowSender (Network network, Address address) { this.address = address; destinationAddress = null; this.network = network; network.AddReceiver (this, address); buffer = new SendBuffer (6 * network.MTU ()); window = new SendWindow (WINDOW_SIZE); currentSequenceNum = 4; synTimeout = null; connectionOpen = false; rttEstimate = TIMEOUT; congestionWindowSize = WINDOW_SIZE; } // Algorithm control public static boolean ACK_FOR_ALL_PREVIOUS = false; public static boolean TIMEOUT_GO_BACK_N = false; public static boolean ACK_WITH_WINDOW = true; // Parameters based on control public static int WINDOW_SIZE = 50; public static int TIMEOUT = 8000; private SendWindow window; private Network network; private Address address; private SendBuffer buffer; private Address destinationAddress; private boolean connectionOpen; private TimeoutID synTimeout; /************************************************************ Open a connection to the ReliableReceiver represented by the address. This call blocks until the connection is established.

For stop-and-wait, true connection setup is not implemented, and this jsut sets the destination @param destination the address of the receiver ************************************************************/ public void Open (Address destination) { destinationAddress = destination; currentSequenceNum = (currentSequenceNum * 37 + 42) % 100; connectionOpen = false; SendSyn (); // This blocks until the connection opens. while (!connectionOpen) Wait (); } private void SendSyn () { ReliablePacket p = new ReliablePacket (); p.sourceAddr = address; p.destinationAddr = destinationAddress; p.sequenceNum = currentSequenceNum; p.ack = false; p.syn = true; p.data = new byte[0]; network.TransmitPacket (p); synTimeout = Timer.global.SetTimeout (this, TIMEOUT, Timer.US, null); } /************************************************************ Send a block of data on the connection. Any amount of data may be sent. This call may block if there is insufficient buffer space. Clients may make any number of Send calls per open connection. @param data the data (any amount) to send on the connection @throws ConnectionNotOpenException if the connection is not open ************************************************************/ public void Send (byte[] data) throws ConnectionNotOpenException { if (destinationAddress == null) throw new ConnectionNotOpenException (); int previous = buffer.AppendBytes (data); if (connectionOpen && previous == 0) // Not currently sending SendMorePackets (); // so start again } /************************************************************ Close the current connection. Blocks until all data is read by the other application and the close operation completes. @throws ConnectionNotOpenException if the connection is not open ************************************************************/ public void Close () throws ConnectionNotOpenException { if (destinationAddress == null) throw new ConnectionNotOpenException (); destinationAddress = null; } /************************************************************ Processing that happens when we receive a packet ************************************************************/ public void PacketArrived (NetworkPacket pk) { ReliablePacket p = (ReliablePacket) pk; // If this isn't an ack, then ignore it if (!p.ack) return; if (p.syn) { if (synTimeout != null) Timer.global.CancelTimeout (synTimeout); connectionOpen = true; NotifyAll (); SendMorePackets (); return; } // Update the round-trip time estimate: int offset = window.FindSeqNum (p.sequenceNum); if (offset != -1) { if (window.xmitTimeAt (offset) > 0.0) { double rtt = Timer.global.CurrentSimTime () - window.xmitTimeAt (offset); rttEstimate = (rttEstimate + rtt) / 2.0; window.SetXmitTimeAt (offset, -0.5); } } // Find the acked packet in the window, if not there, then drop window.MarkSeqNumAsAcked (p.sequenceNum, ACK_FOR_ALL_PREVIOUS); // Send more packets if we can make space SendMorePackets (); } /************************************************************ Processing that happens when we get a timeout ************************************************************/ public void Timeout (Object info) { if (info == null) { SendSyn (); return; } int sequenceNum = ((Integer) info).intValue (); Debug.println ('s', "" + Timer.global.CurrentSimTime () + ": Timeout fired for sequence number " + sequenceNum + ", resending"); int offset = window.FindSeqNum (sequenceNum); if (offset != -1) { window.SetXmitTimeAt (offset, -0.5); rttEstimate *= 1.5; SendPacketFromWindow (offset); } else { Debug.println ('s', " that sequence number is not in window!"); return; } if (TIMEOUT_GO_BACK_N) for (int i=offset+1; i 0 && window.PacketAt (0) == null || window.IsAcked (0)) window.ShiftOutPacket (); while (window.Size () < MaxAdjustedWindowSize ()) { byte[] data = buffer.RemoveBytes (network.MTU ()); if (data.length > 0) { ReliablePacket p = new ReliablePacket (); p.sourceAddr = address; p.destinationAddr = destinationAddress; ++currentSequenceNum; p.sequenceNum = currentSequenceNum; p.ack = false; p.syn = false; p.data = data; window.ShiftInPacket (p, null); window.SetXmitTimeAt (window.Size () - 1, Timer.global.CurrentSimTime ()); SendPacketFromWindow (window.Size () - 1); Debug.print ('s', "" + Timer.global.CurrentSimTime () + ": Sender window: "); for (int i=0; i 0) window.removeElementAt (0); } public int FindSeqNum (int sequenceNum) { for (int i = 0; i < Size (); ++i) if (PacketAt (i) != null && PacketAt (i).sequenceNum == sequenceNum) return i; return -1; } public boolean MarkSeqNumAsAcked (int sequenceNum, boolean markAllPrevious) { // Step 1: find the sequence number and mark it /* int offset; for (offset = 0; offset < Size (); ++offset) if (PacketAt (offset) == null || PacketAt (offset).sequenceNum == sequenceNum) break; */ int offset; for (offset = 0; offset < Size (); ++offset) if (PacketAt (offset) != null && PacketAt (offset).sequenceNum == sequenceNum) break; if (offset < Size () && PacketAt (offset) != null) { DataAtOffset (offset).acked = true; TimeoutID id = DataAtOffset (offset).timeoutID; if (id != null) Timer.global.CancelTimeout (id); } else return false; // did not find it. // Step 2: if needed, mark all previous // NOTE: steps 1 and 2 are separate to make the operation // non-destructive in the case that the sequence number // is not found. if (markAllPrevious) for (int i=0; i= 0 && offset < Size ()) return (SendWindowData) window.elementAt (offset); else return new SendWindowData (); } } class SendWindowData { public boolean acked; public ReliablePacket packet; public TimeoutID timeoutID; public double xmitTime; }