Users Manual of Protocol Stack Attachable Network Simulator (PSANS)
Introduction:
The purpose of writing the PSANS tool was to provide a platform which simulates the physical layer of a network environment, to protocol designers. Specifically, these designers are students taking CIS725. This tool should provide students a platform for assignments based on protocol development.
The tool requires that the user create a driver or protocol stack, which uses the Physical_Layer as an interface to the NCARD module. This fact being stated, it is important to note that this users manual describes how to use the provided classes in correlation with the users code. As there is no output from either the NCARD module or the Physical_Layer with the exception of some possible error messages, it is hard to say what the tool will look like when it runs. This will be different for each implementation.
The following sections will describe the provided classes which the user can use to implement their code. The final section will provide an example of a simple driver.
NCILIB Class:
The NCILIB class is not actually used by the user. It is the actual interface between the Physical_Layer and the NCARD module. Only the Physical_Layer should make calls to this class. However, before you can use this class, you must make a few small changes to some of the constants.
The first of these constants is only needed in the C++ version. It is the constant COM_SEM_KEY. This value is a unique key value for semaphore used to insure that only one command is processed at one time. The should be unique value for each user.
The second constant is for both versions (JAVA/C++). This constant is the HOST_PORT constants. This constant should be an integer value, which represents what port the server is listening for requests. Again, this should be different for each user so there are no conflicts with different NCARD modules.
The third and final constant is for both versions (JAVA/C++), though each version had different representations. This constant is the HOST constants. In the C++ version, this is the decimal form of the ip address (ex: 129.130.10.8). In the JAVA version, this is the String representation of the ip address (ex: zaurak.cis.ksu.edu). This value does not have to be unique for each user.
Physical Layer:
The Physical_Layer is a required class, which provides the interface to the NCARD module. Every program written for the PSANS tool must use this class.
The first step in using the Physical_Layer is to create an instance of the class. To do this, you simply create a variable of type Physical_Layer, and create a new instance. Here are the typical declarations in both C++ and JAVA:
C++ Declaration:
Physical_Layer *pl;
pl = new Physical_Layer(name);
JAVA Declaration:
Physical_Layer pl;
pl = new Physical_Layer(name);
As you can see, the two languages are almost identical. The only difference is that in C++ you want to use a pointer. The parameter name must be provided to the Physical_Layer at the time of creation. This value must be a valid character in the ranges 'A'-'Z' or 'a'-'z'. If the character is not in one of those ranges, the error "Invalid node name" will be produced, and the program will exit.
At this point, the program will call the NCILIB _connect method to open a connection with the NCARD module. If the name parameter is not defined in the Topology file, the NCARD module will refuse the connection, and the _connect method will report the error "Node Name not Found in Topology File", and the program will exit. If the connection was made and excepted, then you are ready to start passing/receiving messages with the NCARD module.
The Physical_Layer class utilizes the NCILIB, providing an interface with the NCARD module. This class consists of 5 main calls. These calls are send, receive, checkBus, address, and noOfLinks.
Physical Layer::address method:
The address method will be used by protocol stacks to determine what node is connected by this link. It will cause a message to be sent out to the NCARD module requesting the address of the link. When the value is received, it is used as the return value.
There are 3 different return values from an address call. If the value is in the ranges 'A'-'Z' or 'a'-'z', this is a point-to-point link to that node. If the value is a number, this is a link to a bus with the number of the value. If the value is the character '!', either an error has occurred in the transmission, or there is no link with that number (this is the most likely reason).
Physical Layer::noOfLinks method:
The noOfLinks method will be used by the protocol stack to determine the number of outgoing links connected to this node. It will cause a message to be sent out to the NCARD module requesting the number of links. When the value is received, it is used as the return value. If this method returns the value -1, there has been an error in transmission and you should try again.
Physical Layer::send / receive methods:
The other two methods are the send and receive methods. These are the methods that are used by the developer to place packets on, and to get packets off of the data channel. The receive method will return the String "_none" if there is no data on channel to receive.
Physical Layer::disconnect method:
There is one final method that is provided by the Physical_Layer. This method should be the last thing that your protocol stack does. It sends a message to the server telling it that you are shutting down. This allows the NCARD module to stop listening to your sockets for messages.
In the C++ version, this method will also remove the semaphore, which is used to insure that only one command is executed at any time. This is an important set since you are using actual system resources, and you need to release them. If you do not use this method, you will still have a semaphore in your name.
Note:
To see if you have any semaphores in your name, you can type the command:
>ipcs
This will return something like this:
PC status from <running system> as of Tue Mar 16 12:34:45 1999
Message Queue facility not in system.
T ID KEY MODE OWNER GROUP
Shared Memory:
m 0 0x50000eeb --rw-r--r-- root root
Semaphores:
s 0 0x000187cf --ra-ra-ra- root root
s 1 0x0000078e --ra-ra-ra- sowens 723
If there is a semaphore listed in your name, you can remove it by using the number. In this case, my semaphore is number 1. So I would type:
>ipcs -s 1
This will kill my semaphore.
To make this easier, a script file semkill has been included which will kill all semaphores you might have running.
Helper Classes:
A few helper classes have been created for developers to use in the creation of the protocol stack code. The classes are the Semaphore class, Timer class, D_Buffer class, and an example Thread class.
Semaphore Class:
A Semaphore class (written by Dr. Singh) has been provided for the JAVA code. It simulates the Unix semaphore for JAVA. It provides the P and V operations to signal and wait for a semaphore. Unlike the Unix semaphore, this is not an actual system resource. It is just simulated as a class.
For C++, there is a similar sem.h which provides sem_wait and sem_signal operations on actual semaphores.
Timer Class:
The timer class is used to simulate a system clock timer. It provides methods to start the timer, stop the timer, reset the timer, and check for a time out. The timeout is not a physical system timeout, but simply a boolean check to see if the timer plus the interval is greater than the current time.
To create a timer, simply create a variable of type Timer and supply an interval value in milliseconds. A sample JAVA declaration is as follows:
Timer t = new Timer(50);
The C++ declaration would again be the same except using a pointer.
Now you are ready to use the timer. A sample use might be like this:
t.startTimer();
while(!t.timeOut());
t.stopTimer();
As stated before, this timer does not have a physical system timeout. Therefore, unless you call the timeOut method, you will not know that the timeout has occurred. For this reason, this timer is not intended to be used to control time critical events. It is simply used to control the minimum wait time of an activity.
D_Buffer Class:
Probably the most used, of the helper classes, is the D_Buffer class. This is class, which implements a circular queue. In a typical implementation of a protocol stack, each layer probably has two of these buffers, one for each outgoing and incoming messages.
D_Buffer::enqueue method:
The class provides two methods for en-queuing, a blocking and non-blocking form. Both take a String as a parameter, and place it at the end of the queue. The difference between the two methods is that the non-blocking enqueue() will return false if queue is full and it can not place the item in the queue. The blocking enqueueb() will continue loop until it has successfully placed the item at the end of the queue.
D_Buffer dequeue method:
The class also provides two methods for de-queuing, a blocking and non-blocking form. Both remove a String from the front of the queue and return the value to the caller. The difference between the two methods is that the non-blocking enqueue() will return the string "__empty" if queue is empty. The blocking enqueueb() will continue loop until it has successfully removed an item from the front of the queue.
D_Buffer::isData/isFull methods:
The D_Buffer class provides two boolean functions to check the state of the buffer. The method isData check to see if queue has any data to return, while the method isFull, checks to see if the queue is full. Both can be very useful to the developer in deciding what actions to perform based on the state of the queue.
D_Buffer::setupBuffer method:
This method is used to specify the size of the buffer. It is intended to be used only once in the life of the buffer. The parameter size is passed in specifying the size of the queue. This method will then create a new array with this number of elements. For this reason, if the buffer is already in use, it will be lost and a new buffer will be created.
NCARD Module:
The NCARD module should be supplied for you as JAVA class file. To execute the module, simply use your normal JAVA execution method (usually java <file><params>) with NCARD as the file name. You do need to provide one parameter representing the port that the server should listen to. This should correspond to the number that you use as the HOST_PORT constant in your Physical_Layer class. Using this number as a parameter allows the user to be able to use the NCARD module without the need to recompile the code.
Template Thread Class:
This class is mainly intended for those who do not know how to create a thread. The developer can copy this class and give it a new name. They can then customize it to their own needs. As is, this class will do nothing, it is simply a template.
Topology File:
The topology file is a text file, which maps out the network architecture. The file should list all the nodes on the network. It also lists all the lines connecting the nodes. This file must be present in the same directory as the class files for the NCARD module, and must be named Topology. If the NCARD module is being run on a Unix machine, the file must be named exactly the same using the same cases for each letter (Uppercase T and the rest lowercase).
The Topology file is divided into 5 parts consisting of: Loss Probability, Reorder Probability, Node Definitions, Bus Definitions, and Link Definitions. The end-of-section marker --- should separate each section. A comment lines is any line starting with the # character. Comment lines are allowed anywhere in the file.
The first section, loss probability, and the second section, reorder probability, should each consist of only one line. This line should be a valid number between 0 and 100. The larger the number, the more chance that data will be lost or reordered. An typical declaration might look like this:
#Loss Probability
10
---
#Reorder Probability30
---
The third section is the Node Definition section. This section defines each node and its buffer size. The first line of the section must be a single number representing the number of nodes that are in the network. Following this line, should be the actual declaration line. These lines should consist o the node name and the buffer size. Node names must be in the range of a-z or A-Z. The buffer size must be a positive number greater than 0. There should be the same number of these lines, as the number that was specified at the top of the section. A typical declaration might look like the following:
#Total number of nodes
4
# Node entries
# Format: node buffer_size A 10B 10C 10
D 10
---
The fourth section is the Bus Definition section. This section defines each bus and its length. The first line of the section must be a single number representing the number of buses that are in the network. Following this line, should be the actual declaration line. These lines should consist of the bus number and the bus length. The bus number must be a positive number (0-n). The bus number must be a number greater than 0. The larger this number, the longer the data is held before sending it out on the bus. There should be the same number of these lines, as the number that was specified at the top of the section. A typical declaration might look like the following:
#Total Number of Buses
1
The final section in the Topology file is the Link Definitions. This section consists of multiple smaller section defining links. The Link Definition section must contain at least one link declaration. Each set of link definitions start with a number stating the number of links and the type of the link. The number of links must be a positive number. The type must be either PTP of Bus. Following this line should be the actual link declaration. There should be the same number of declarations as the number that was defined in the link type line.
A point-to-point link definition consists of the start node, end node, line number, and capacity. The start and end nodes must be in the range a-z or A-Z. The nodes must be defined in the Node Declaration section. The line number must be a positive number. No node can have two links with the same line number (point-to-point or bus). The capacity must a positive number. The larger this number, the shorter the wait between messages being sent out.
A bus link definition consists of the node name, line number, and bus number. The node name must be in the range a-z or A-Z. The nodes must be defined in the Node Declaration section. The line number must be a positive number. Again, no node can have two links with the same line number (point-to-point or bus). The bus number must be positive, and must be defined in the Bus Declaration section. A typical declaration might look like the following:
#Total links for node
#Format: Number Type_of_Connection
2 PTP
#Each Link
#Format: Start_Node End_Node Line_number CapacityA B 0 10A C 1 15
1 BUS#Bus entry
#Format: Node Line_Number Bus_Number
A 2 1
BNF for Topology file:
// ascii(n) represents the character whose ascii value equals the parameter n
<digit> ::= 0..9
<prob> ::= <digit>
| <digit><digit>
| 100
<num> ::= <digit>+
<node_name> ::= a..z
| A..Z
<EOS> ::= ascii(45) ascii(45) ascii(45) //---
<space> ::= ascii(32)
<anything> ::= <space> .. ascii(254)
<comment> ::= # <anything>*
<Topology> ::= <loss_prob><reorder_prob><nodes><buses><links>
<loss_prob> ::= <comment>*<prob><comment>*<EOS>
<reorder_prob> ::= <comment>*<prob><comment>*<EOS>
<nodes> ::= <comment>*<num><node_entry>+ <comment>*<EOS>
// where 0< num < 53 and the value of num is how many <node_entry>'s
//are present
<node_entry> ::= <comment>*<node_name><space><num>
<buses> :: = <comment>* 0 <comment>*<EOS>
| <comment>*<num><bus_entry> * <comment>*<EOS>
// where 0< num and the value of num is how many <bus_entry>'s
//are present
<bus_entry> ::= <comment>*<num><space><num>
<links> ::= <comment>*<PTP_group><BUS_group><comment>*<EOS>
<BUS_group> ::=
Ø| <num><space>BUS<BUS_entry> *
// where 0< num and the value of num is how many <BUS_entry>'s
//are present
<PTP_group> ::=
Ø| <num><space>PTP<PTP_entry> *
// where 0< num and the value of num is how many <PTP_entry>'s
//are present
<PTP_entry> ::= <comment>*<node_name><node_name><num><num>
<BUS_entry> ::= <comment>*<node_name><num><num>
Sample Driver:
Though the purpose of this tool is to test a protocol stack, I have provided just a small driver in this manual to save space. A full stack would probably consist of many classes, each running as a separate thread, which pass messages from one layer to the next.
The following is a sample program that shows how to get started. This particular set of code implements a sliding window protocol. It shows how to use create the Physical_Layer, get all the links, and send data using a timer.
import java.awt.*;
import java.io.*;
import java.util.*;
class SP
{
Physical_Layer pl;
int no_lines;
int[] lineno_table;
char[] node_table;
int ns = 0;
int na = 0;
int w = 3;
String [] data;
Timer t = new Timer(w*50);
public void driver(char name)
{
boolean done = false;
boolean all_acked = false;
pl = new Physical_Layer(name);
setupLinks();
data = new String[w*2];
data[0] = "Message 0";
data[1] = "Message 1";
data[2] = "Message 2";
data[3] = "Message 3";
data[4] = "Message 4";
data[5] = "Message 5";
do
{
String msg;
if ((na + w) > ns && !done)
{
int s = ns%(2*w);
if (ns == 27)
data[5] = "__LAST MESSAGE__";
msg = ns + " " + data[s];
if (data[s].equals ("__LAST MESSAGE__"))
done = true;
System.out.println("Sending: " + msg);
pl.send(msg,0);
ns++;
t.startTimer();
}
else
{
msg=pl.receive();
if (!msg.equals("_none"))
{
StringTokenizer st = new StringTokenizer(msg);
String t_1 = st.nextToken();
String t_2 = st.nextToken();
int n = Integer.parseInt(t_2);
if (n > na)
{
na = n;
t.resetTimer();
if (done && na == ns)
{
all_acked = true;
t.stopTimer();
}
}
}
else
if (t.timeOut())
{
System.out.println("TIMEOUT");
ns = na;
done = false;
all_acked = false;
t.resetTimer();
}
}
} while (! all_acked);
pl.disconnect();
System.out.println("Server Disconnected");
}
private int getLine(char c)
{
int loc = -1;
for (int i = 0; i< no_lines; i++)
{
if (node_table[i] == c)
loc = i;
}
return loc;
}
public void setupLinks()
{
no_lines = pl.noOfLinks();
node_table = new char[no_lines];
lineno_table = new int[no_lines];
int line_no = 0;
char t;
for (int i = 0; i<no_lines; i++)
{
t = '!';
do
{
t= pl.address(line_no++);
}
while (t == '!');
System.out.println("Line: " + Integer.toString(line_no-1) + " Node: " + t);
lineno_table[i] = line_no-1;
node_table[i] = t;
}
}
public static void main(String args[])
{
SP s = new SP();
s.driver('A');
}
}
Error Messages:
The following sections will explain the errors that can occur in the system which will result in the system terminating. The errors are divided between the NCARD module and the client side (User's protocol side).
NCARD Module Errors:
Topology File Not Found:
This error is raised when the NCARD module can not find the Topology file. Make sure that the file is named exactly 'Topology'. It also must be in the directory where the NCARD module class is found.
Topology File Error on line <X>:
This error is raised when an error is found in the Topology file. The line number that is reported is the line that caused the error to be raised. To determine how to fix this, refer to the Topology file section above. Make sure that the line is in the correct format. There is no easy answer to this error. You must use the BNF rules and the English narrative to determine what is wrong.
Node <X> does not have outgoing link to <Y>:
This error is raised for an unmatched link in the Topology. For each node, if the node has a link to another node, that node must also have a link back to the original node.
Ex:
If we have the link declaration:
A B 0 10
There must be a corresponding link like:
B A 2 25
Client Side Errors:
Invalid node name:
This error is raised when Physical_Layer is created with an invalid node name. The node name must be in the range 'A'-'Z' or 'a'-z'. If this error is raised, check the line of code where the Physical_Layer is created. Make sure the parameter passed is in the above ranges.
Node Name not Found in Topology File:
This error is raised when the node name passed into the Physical_Layer is not defined in the node section of the Topology. If this error is raised, check the line of code where the Physical_Layer is created. Make sure the parameter passed is defined in the Topology file.