package link; import java.io.*; import java.util.*; import debug.*; import timer.*; /***************************************************************** *****************************************************************/ /************************************************************ ************************************************************/ /***************************************************************** A simple point to point link that implements a randomized delivery model. No reordering is ever permitted, which does alter the simple latency model somewhat. *****************************************************************/ public class SimplePointToPointLink extends TimerClient implements Link, Runnable { /************************************************************ Create a new link with the specified parameters @param name the debug name for this link @param seed the seed for the random number generator @param mtu the maximum transmission unit, in bytes @param bandwidthMean the long-term average bandwidth, in Mb/s @param bandwidthSD the standard deviation on the bandwidth distribution @param bandwidthWalk is the tendency towards random walks, from 0.0 to 1.0 @param latencyMean the long-term average latency, in ms @param latencySD the standard deviation on the latency distribution @param latencyWalk is the tendency towards random walks, from 0.0 to 1.0 @param dropProb is the probability that packets are dropped @param downtime is a vector of TimeIntervals during which this link will be unavilable. If null then it will always be available ************************************************************/ public SimplePointToPointLink (String name, long seed, int mtu, double bandwidthMean, double bandwidthSD, double bandwidthWalk, double latencyMean, double latencySD, double latencyWalk, double dropProb, Vector downtime) { this.name = name; rand = new Random (seed); this.mtu = mtu; this.bandwidthMean = bandwidthMean; this.bandwidthSD = bandwidthSD; this.bandwidthWalk = bandwidthWalk; this.latencyMean = latencyMean; this.latencySD = latencySD; this.latencyWalk = latencyWalk; this.dropProb = dropProb; this.downtime = downtime; linkIsUp = true; bandwidthLast = bandwidthMean; nodes = new Vector (); currentTransmissions = new Hashtable (); recipientClearTimes = new Hashtable (); if (downtime != null) { Thread downtimeThread = new Thread (this); Timer.global.RegisterThread (downtimeThread); downtimeThread.start (); } } /************************************************************ Convert to a string for printing ************************************************************/ public String toString () { return "SimpleP2P: " + name; } private String name; private Random rand; private int mtu; private double bandwidthMean; private double bandwidthSD; private double bandwidthWalk; private double bandwidthLast; private double latencyMean; private double latencySD; private double latencyWalk; private double latencyLast; private double dropProb; private Vector nodes; // all connected nodes private Hashtable currentTransmissions; // node -> packet private Hashtable recipientClearTimes; // node -> time in us private Vector downtime; private boolean linkIsUp; /************************************************************ Get the list of connected nodes @return an Enumeration of the currently connected nodes. ************************************************************/ protected Enumeration ConnectedNodes () { return nodes.elements (); } /************************************************************ Transmit a packet on the link. Samples to find the transmit time, then waits for that time to simulate transmission. While one node is transmitting all others are blocked. Fair queuing is not attempted. The packets arrive at the other end after the latency (also sampled), with the constraint that they arrive in order and not exceed the arrival bandwidth.

Different nodes may be transmitting at the same time, but any particular node is limited to transmitting a single packet at a time. Attempts for a single node to transmit multiple packets in parallel will block. @param p the packet to transmit @param n the node (router) sending the packet @see Link#TransmitPacket ************************************************************/ public void TransmitPacket (LinkPacket p, Node n) { if (!linkIsUp) return; Debug.println ('l', "" + Timer.global.CurrentSimTime () + ": TransmitPacket from node " + n + " on link " + this); if (p.data.length > MTU ()) return; double transmitTime; long arrivalTime; if (!nodes.contains (n)) return; while (currentTransmissions.get (n) != null) Wait (); currentTransmissions.put (n, p); bandwidthLast = LinkUtils.SampleDistribution (rand, bandwidthMean, bandwidthSD, bandwidthWalk, bandwidthLast); latencyLast = LinkUtils.SampleDistribution (rand, latencyMean*1000, latencySD*1000, latencyWalk, latencyLast); transmitTime = (p.data.length * 8.0) / bandwidthLast; // in us // Time that this packet will start arriving arrivalTime = Timer.global.CurrentSimTime () + (long)latencyLast; // simulate transmission time, and allow others into the // monitor during transmission. The flag on currentTransmissions // prevents incorrect interaction Timer.global.WaitFor (this, (long) transmitTime, Timer.US); if (rand.nextDouble () >= dropProb) { long clearTime = ((Long) recipientClearTimes.get ( OtherNode (n))).longValue (); if (arrivalTime < clearTime) latencyLast = clearTime + (long) transmitTime - Timer.global.CurrentSimTime (); Debug.println ('p', "Packet xmit: " + n + " -> " + OtherNode(n) + " " + p + " sent @" + (long)(Timer.global.CurrentSimTime () - transmitTime) + " clrd @" + (long)(Timer.global.CurrentSimTime () + 0) + " arrv @" + (long)(Timer.global.CurrentSimTime () + (long)latencyLast)); Timer.global.SetTimeout (this, (long)latencyLast, Timer.US, new ArrivalRecord (p, OtherNode (n))); // Time that this packet will stop arriving recipientClearTimes.put (OtherNode (n), new Long (arrivalTime + (long) transmitTime)); } else { Debug.println ('p', "Packet xmit: " + n + " -> " + OtherNode (n) + " " + p + " sent @" + (long)(Timer.global.CurrentSimTime () - transmitTime) + " clrd @" + (long)(Timer.global.CurrentSimTime () + 0) + " DROPPED"); } currentTransmissions.remove (n); NotifyAll (); } /************************************************************ Deliver a packet. Timeouts are used to deal with the latency, so when the timeout fires, deliver the packet. @param info an ArrivalRecord describing the packet to deliver ************************************************************/ public void Timeout (Object info) { ArrivalRecord rec = (ArrivalRecord) info; rec.node.PacketArrived (rec.packet, this); } /************************************************************ Get the maximum number of data bytes to transmit @return the maximum transmission unit, in bytes @see Link#MTU ************************************************************/ public int MTU () { return mtu; } /************************************************************ Get the average bandwidth for this link @return the bandwidth, in Mb/s @see Link#Bandwidth ************************************************************/ public double Bandwidth () { return bandwidthMean; } /************************************************************ Get the average latency for this link @return the latency, in ms @see Link#Latency ************************************************************/ public double Latency () { return latencyMean; } /************************************************************ Get the names of all the other nodes connected to this link (i.e. all the nodes who would receive a packet that was transmitted on the link) @param n the node making the request (so that it can be excluded from the result) @return an array of the names of the other nodes ************************************************************/ public String[] OtherNodeNames (Node n) { String[] retval = new String[1]; retval[0] = "" + OtherNode (n); return retval; } /************************************************************ Add a node, maintains the list of connected nodes. At most two nodes may be connected at any time. @see Link#AddNode ************************************************************/ public void AddNode (Node n) { if (nodes.size () < 2 && !nodes.contains (n)) { nodes.addElement (n); recipientClearTimes.put (n, new Long (Timer.global.CurrentSimTime ())); } } /************************************************************ Remove a node @see Link#RemoveNode ************************************************************/ public void RemoveNode (Node n) { nodes.removeElement (n); recipientClearTimes.remove (n); } private Node OtherNode (Node n) { Node retval = null; if (nodes.size () == 2) { if (nodes.elementAt (0) == n) retval = (Node) nodes.elementAt (1); else retval = (Node) nodes.elementAt (0); } return retval; } /************************************************************ Manage uptime and downtime of the link. Alert registered clients when the link comes and goes, adn block further transmission (to be nice, current transmissions complete). ************************************************************/ public void run () { synchronized (Timer.global) { while (downtime.size () > 0) { TimeInterval t = (TimeInterval) downtime.elementAt (0); Enumeration e; Timer.global.WaitUntil (this, t.start, Timer.US); Debug.println ('L', "" + Timer.global.CurrentSimTime () + ": link " + this + " going down"); linkIsUp = false; e = ConnectedNodes (); while (e.hasMoreElements ()) ((Node) e.nextElement ()).RemoveLink (this); Timer.global.WaitUntil (this, t.end, Timer.US); Debug.println ('L', "" + Timer.global.CurrentSimTime () + ": link " + this + " going up"); linkIsUp = true; e = ConnectedNodes (); while (e.hasMoreElements ()) ((Node) e.nextElement ()).AddLink (this); downtime.removeElementAt (0); } } Timer.global.UnregisterThread (Thread.currentThread ()); } } class ArrivalRecord { public ArrivalRecord (LinkPacket p, Node n) { packet = p; node = n; } public LinkPacket packet; public Node node; }