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