cnet Frequently Asked Questions

This page contains a number of Frequently Asked/Answered Questions (FAQs) about the cnet network protocol simulator. The questions here have been collated from students' actual questions over recent years. Please read this page first to ensure that you fully understand what is happening and then be able to anticipate any errors that you may confront.

Please email if you find any errors here, or think that some additional material should be added.

Getting started


Developing protocols

Managing time and periodic activity

Hidden meanings

Getting some real protocol simulation done

How can I get started with cnet?

There are many on-line WWW pages to help you to get started with cnet. Allow about two hours to read each of these links and this FAQ:

Firstly, read the cnet specific header file (typically installed at /usr/local/include/cnet.h). All cnet programs must include the line

     #include <cnet.h>

to include the contents of this file. In particular, it is important that you understand the CnetNodeinfo and CnetLinkinfo structures, the CnetEvent and CnetError enumerated types and the protoypes of commonly used functions. These are all described in much greater detail in another set of web-pages:

Where can I get cnet for Windows?

cnet does not currently run under Windows, and there are no current plans to port cnet to Windows. All of cnet's source code is released under the GNU Public Licence (GPL). Good luck.

How can I compile my cnet protocol source files?

You do not need to compile your protocol files yourself. Simply executing

    cnet TOPOLOGY

will cause cnet to locate and compile your C source files and produce a shared object file in your working directory (e.g. protocol.cnet). This shared object is then dynamically linked with the cnet simulator at run-time.

The system's standard C compiler is used, preferably GNU's gcc. All C error messages are reported by the compiler (not cnet). All fatal and warning messages should be eliminated before your protocol code can be considered syntactically correct. You will probably receive many more error messages than you've experienced before - the reason being that the compiler is invoked with extra compilation switches to be very pedantic (this is good for your soul and in fact is how you should always compile C code). If you are concerned about any "black magic" destroying your code, observe what happens by invoking cnet as:

    cnet -d TOPOLOGY

How can my protocols access external functions?

Protocols written in cnet may access external functions, such as functions in the cryptographic library. For example, to access the old UNIX crypt function, we first need to add function prototypes to our protocols - this is usually involves including a header file in our C code:

    #include <unistd.h>

and then indicating in our topology file that we need to link against an external library, with (for example):

    compile = "protocol.c -lcrypt"

The embedded switches -l and -L, and any filenames ending in .o or .a, are recognized as (assumed to be) arguments for the linker. All other switches are assumed to be C-preprocessor and C-compiler switches.

NOTE: By default, cnet's header file includes the header file <math.h>, and links against the standard C mathematics library by appending -lm.

Why does cnet terminate after 5 minutes?

Errors in your protocols which prevent an event handler from returning when expected, prevent the cnet scheduler from performing correctly. In particular, the scheduler cannot service events from the windowing system - for example your requests to kill cnet itself when you detect a problem. To overcome this major problem, cnet itself times-out after 5 minutes just in case you have written incorrect protocols which have become 'stuck'. Once you are confident that your protocols are working as expected you can easily extend this 5 minute period with, for example,

    cnet -m 10 TOPOLOGY

where the command line option requests execution for the required number of minutes.

How can I develop my cnet protocols in multiple files?

As cnet projects become larger, it's naturally wise to develop protocols in a number of different source files. A natural method to partition the files is based on their responsibilities. C (and of course many other programming languages) allow you to place relatively independent sections of source code in separate files, compile each source file individually, and to then link the resulting object files to form a single executable file.

cnet also allows you to do this, but simplifies the activity. In your topology file, replace a line such as

        compile = "protocol.c"
with	compile = "dll.c nl.c routing.c queueing.c fragments.c"

(or whatever) and cnet will quietly compile and link all of the pieces. Only one of the C source files needs to have a reboot_node() function.

cnet handles the compilation and linking quite happily, unless it is interrupted. If individual files appear to be not being compiled, just remove all object files with rm *.o and re-run cnet. If you're interested in what's going on, invoke cnet with its -v switch to see the executed compilation and linking commands.

How many nodes are there in my simulation?

By default, cnet does not reveal the total number of nodes in the simulation. This encourages the development of protocols which make no assumptions about the size of the network.

By setting the -N command line option, the number of nodes in the simulation is available in the global integer variable NNODES. Without -N, the value of NNODES is zero.

How many links does my node have?

The number of physical links connecting a node (i.e. those of type LT_WAN, LT_LAN, or LT_WLAN), is always available in the global integer variable nodeinfo.nlinks.

Links are numbered within each node from 0 to nodeinfo.nlinks. Link number 0 is always the node's LOOPBACK link, which simply delivers anything transmitted to it straight back to the same node.

The first physical link is number 1, and every node will have a link number 1. Each node's link attributes may be accessed, at runtime, via elements of the C structure variables linkinfo[0], linkinfo[1], linkinfo[2], and so on.

When allocating an array of structures, and then iterating across all physical links indexed by their link number, you'll require code similar to:

        linkthings = calloc(nodeinfo.nlinks+1, sizeof(LINKTHING));
        for(link=1 ; link<=nodeinfo.nlinks ; ++link)
            linkthings[link].field = ....

What is the difference between a node's number and its node address?

Nodes have both a number and an address - node numbers (available in nodeinfo.nodenumber) range from 0,1,2,.....NNODES-1, whereas each node's address (available in nodeinfo.address) can be any unique non-negative value. By default node numbers and node addresses are the same (0,1,2,....).

Setting a node address attribute in the topology file, as with

    host Perth {
        address     = 351

should reveal a problem if your protocols are assuming that node numbers and node addresses are always the same. In particular, the destination node addresses provided by CNET_read_application and expected by CNET_write_direct are node addresses and not node numbers.

What is link zero ever used for?

Links are numbered within each node from 0 to nodeinfo.nlinks. Link number 0 is always the node's LOOPBACK link, which simply delivers anything transmitted to it straight back to the same node.

The LOOPBACK link may be used when developing and debugging protocols. A node may construct, "transmit", and instrument data to itself, to debug and test code receiving erroneous data, without sending that data across the physical layer.

What if an event occurs while I'm currently handling an event?

cnet does not employ pre-emptive scheduling - once an event handling function is being executed, it will not be interrupted by the arrival of another event.

Event-handling functions must execute to their completion in a timely manner - they must perform their actions and then simply return. Only then, will other pending events be delivered to their event handlers.

This style of event-driven programming makes protocol development much easier. Your code does not need to manage interrupts or semaphores, or employ critical regions.

Why does my simulation, and the whole GUI, appear to hang?

If one of your event handlers becomes stuck in an unbounded loop, or does not return, the whole simulation and its GUI will appear to hang. The flow of execution must return to cnet's scheduler, in a timely manner, so that any interaction with the GUI is received and further cnet events are raised. Your process is probably still executing, but one of your protocol's event handlers is performing all activity.

You will need to terminate cnet from the command-line (probably with Control-C). Then, re-run the simulation with cnet's trace window displayed. This will provide an annotation of each node's event handlers and the cnet API's invoked while your event handlers are executing.

How can I debug my cnet programs?

Because many things appear to be happening simultaneously in cnet, debugging cnet protocols can be difficult. However, it is far easier than debugging protocols on different computers in different geographic locations!

Although cnet permits protocols to be written in C and executed natively in a single operating system process, it does not provide traditional debugging support. Moreover (unfortunately) because of the way cnet manipulates the single process's data segments to transparently schedule nodes, standard debuggers, such as gdb, can't "keep up" with what is happening and begin reporting incorrect information.

Most importantly, most cnet functions return an integer indicating their success or failure (0 for success, -1 for failure). IT IS ESSENTIAL that you examine the function's return value to ensure that it performed as expected. If you ignore this return value your protocols may fail at a much later stage in their execution and it will be extremely difficult to track down your error. If a function detects an error (and returns -1) it will also set the node-specific variable cnet_errno to reflect what went wrong. The most recent error detected by cnet may then be printed from each node (to stderr) with the function CNET_perror or you may construct your own error messages using the error descriptions in *cnet_errname[] or *cnet_errstr[].

It is also helpful to trace your protocols to see the exact ordering and arguments of cnet function calls. Tracing may be selected with the -t command line option, setting the trace node attribute to true for all or individual nodes in the topology file or by selecting the trace checkbox on either the default or specific node panels under the windowing system. Tracing will appear on the trace stream of cnet (either the separate Tcl/Tk trace window or the shell's tty) and shows each node's event handling functions being invoked (and returned from) and, within each handler, all function calls, their arguments and the function return values. Any function arguments that are modified by the functions (arguments passed by reference) are also shown after the function return values. If any errors are detected by the functions themselves, these will be reported within the trace. See Tracing cnet's execution.

As a special case, networks of only 2-nodes may request that all datalink frames traversing the Physical Layer be drawn in a special window. Drawing frames requires a small addition to the protocol's topology file, and a special event handler to describe how the frames are to be drawn. A careful choice of colours and frame (field) lengths can assist in the debugging of Data Link layer protocols. See Drawing datalink frames in cnet.

Where did my code's output go?

All output implicitly sent to C's standard output stream with calls to printf() or puts(), appears on each node's output window. Clicking a node's icon on the simulation map will reveal this window, and you can scroll back through a limited number of previous lines.

Each node's output printed via printf() or puts() can also be copied to an individual file using the -o option to cnet. For example, if running a two node network with

    cnet -o debug TOPOLOGY

all output will be copied (typically) to the files debug.node0 and debug.node1.

All output explicitly sent to the stdout or stderr streams, requested with calls to fprintf() or fputs(), appears on the invoking terminal/shell window (tty or pty). Such output (merged output from all nodes) can be captured using traditional output redirection in your shell.

Are there any simple tricks that can help my understanding?

Many people get confused with cnet's apparent ability to manage multiple nodes simultaneously within a single process (which is, in fact, one of its unique features). In addition, it can be initially confusing to understand how a single protocol can act as both a sender and receiver simultaneously. A simple trick to ease this confusion is to only allow one node to transmit and the other to receive (in a network of just 2 nodes). As nodes have nodenumbers of 0, 1, 2, ... adding the lines

    if(nodeinfo.nodenumber == 0)

to your handler for EV_REBOOT, typically reboot_node(), will only permit node #0 to generate messages for delivery.

What is the EVENT_HANDLER function that appears in most examples?

EVENT_HANDLER is actually not a function provided by cnet (or your operating system) but a C macro defined in the <cnet.h> header file. Its full definition is:

#define EVENT_HANDLER(name)     \
        void name(CnetEvent ev, CnetTimerID timer, CnetData data)

This provides greater readability in protocol code, which can simply declare its event handling functions with, for example:


Use of EVENT_HANDLER does, however, introduce some potential confusion. As an consequence of being declared with EVENT_HANDLER, each such function "automatically" has three formal parameters: ev, timer and data. While this improves both readability and consistency, it may introduce confusion as to where the parameters actually "come from".

What is the CHECK function that appears in most examples?

CHECK is actually not a function provided by cnet (or your operating system) but a C macro defined in the <cnet.h> header file.

Most of cnet's library (builtin) functions return 0 on success and something else, typically -1, on failure. In fact, if any of these functions fail, it probably indicates a serious error in a protocol (there are a few exceptions to this generalization, such as cancelling a timer that has already expired). Moreover, all functions will set the global error variable cnet_errno on failure and this may be used as an index into the globally accessible array of error strings, cnet_errstr. This is similar to the use of errno and sys_errlist in C.

By enveloping most calls to cnet's library routines with CHECK, we can get an accurate and immediate report on the location (source file, function name, line number, and nodename) and type of each error. These values are passed to cnet's function CNET_exit which, if able, pops up a window highlighting the exact location of the runtime error. Looking at the definition of CHECK in <cnet.h> may expose the "black magic":

#define CHECK(call)     do{ if(call) != 0) \
                        CNET_exit(__FILE__, __func__, __LINE__);} while(false)

CHECK may not strictly belong in cnet's header file, but it's such a useful macro, it saves everyone from re-inventing the wheel.

Can I see my protocol's datalink frames?

cnet presents a limited visualization of data frames traversing any LT_WAN link. Using just colours and lengths, it is possible to display both data and acknowledgment frames, and the contents of some of their fields. In combination, these features may be used to debug implementations of Data Link Layer protocols.

You may view a LT_WAN link's frames by clicking the right mouse button (control-click on Macintoshes) on the link. cnet provides a simple default representation of frames which may be overridden by defining an EV_DRAWFRAME event handler. See also Drawing data frames in cnet.

How can I determine the current time?

Do not attempt to use your operating system's time() or gettimeofday() functions.

In cnet each node's "system-time" is provided in the global integer variable nodeinfo.time_in_usec, which measures the time, in microseconds, since the node was last rebooted. This value will (usually) have increased between calls to event handlers, but its value will not change during the execution of an event handler.

The current time of day, i.e. the "wall clock time" of each node, is available via the structure nodeinfo.time_of_day, i.e. in nodeinfo.time_of_day.sec and nodeinfo.time_of_day.usec. The integer value in nodeinfo.time_of_day.sec represents the number of seconds elapsed since 00:00:00 on January 1, 1970, and can thus be used as an argument to standard operating system functions such as ctime().

Unless cnet is invoked with the -c option, the wall clock time of all nodes is initially different on each node. If -c is specified, the clocks in all nodes will initially be synchronized. Protocols may be developed, which call CNET_set_time_of_day to synchronize the clocks.

cnet provides no support for timezones or daylight saving.

How can I print a CnetTime value?

All times in cnet are measured in microseconds, and the datatype CnetTime is a 64-bit integer - long enough to hold about 580,000 years of simulation. To print a value of CnetTime, we have two choices:

       printf("time = %lld usecs\n", value);
       printf("time = %s usecs\n", CNET_format64(value));

where the CNET_format64 function formats the value with commas included every 3 digits to increase the readability.

What are timers and CnetTimerIDs?

The event-driven nature of cnet means that your protocols cannot simply 'wait' for something to happen. The cnet scheduler will inform your protocols when important things need doing (messages to deliver, frames to receive, etc). In particular, your protocols cannot simply wait a nominated period of time and then do something appropriate after that time.

YOUR PROTOCOLS SHOULD NOT CALL sleep() or any similar functions. Instead, cnet provides timers so that the scheduler may inform your protocol when a nominated period of time has elapsed. You may have an almost unlimited number timers quietly "ticking away" - they are each uniquely identified by a CnetTimerID.

When you create a new timer you must indicate one of 10 timer events EV_TIMER0..EV_TIMER9 and a period of time (in microseconds) in the future. The function CNET_start_timer will return to you a CnetTimerID so that you may keep track of which timer has expired when your timer event handler is invoked. For example:

    CnetTimerID  saved_timer;

    saved_timer = CNET_start_timer(EV_TIMER1, (CnetTime)1000000, 0);

will cause the event handler for EV_TIMER1 to be called in 1 second. The value of saved_timer will be passed as the second parameter to the handler so that you can see which timer expired. You can have as many outstanding timers on the EV_TIMER1 queue as you want. PLEASE NOTE: there are not only 10 timers available - however, each timer must be tagged with one of the 10 available timer events.

If you decide that you no longer want to be informed when a timer expires, you should call CNET_stop_timer with the CnetTimerID in which you are no longer interested. For example,


If the cnet scheduler invokes your timer handler, then you do not need to cancel the corresponding timer (it will be done for you). However, if you wish a timer event to be raised periodically, then you'll need to start a new timer in the handler of an expiring timer, i.e. timers only expire once, not repeatedly.

Why does cnet only provide 10 timers?

cnet provide 10 distinct timer queues - not just 10 timers. Each timer queue may have an unlimited number of timers "ticking-away" on it and, when each expires, the event handler for the associated queue will be invoked.

When writing protocols in multiple layers, it's a nice separation of concerns to employ different timers in each layer. For example, in a Data Link Layer protocol, we could use EV_TIMER1 for a frame retransmission timer, and EV_TIMER2 for a piggyback timer. At the same time, the Network layer may use EV_TIMER3 to flush any queued packets if it uses a leaky bucket for congestion control, and EV_TIMER4 to periodically exchange routing table information with neighbours.

Employing a distinct timer queue for each activity allows us to use a separate handler to manage the requirements of each activity, and to "hide" the handler in the protocol layer or source file of concern.

What is the third parameter to CNET_start_timer ever used for?

Any value passed as the third parameter to CNET_start_timer is remembered, internally, along with the timer. When the timer expires, this saved value is passed as the third parameter to the handler for the timer's event. This parameter is of type CnetData (a long integer in C) which allows it to store integral values or a pointer to a variable or dynamically allocated data structure. Typical uses for this parameter are to pass a sequence number used in a protocol, or perhaps a pointer to a list or tree structure, to the timer event's handler.

If the parameter is used to store a pointer, care must be taken to ensure that the pointer is still valid at the time the timer's event handler is called. In particular, the parameter should never be used to store the address of any variable or structure on C's runtime stack.

It is reasonable to pass a pointer to dynamically allocated storage to CNET_start_timer (i.e. allocated with malloc), and then have the timer's event handler deallocate this storage (i.e. deallocated with free).

If you need to call CNET_stop_timer before a timer expires, take care to first deallocate any dynamic storage associated with the timer as a CnetData value. You can "recover" the CnetData value by calling the function CNET_timer_data.

Can I add my own CnetEvent events?

No, not unless you wish to change and recompile the source code of cnet itself. However, there are a few "spare" standard EV_TIMER events that could be re-used or "renamed" to suit your purpose. For example, if you'd like a new event for the Data Link Layer to signal the Network Layer, you could (ab)use the C-preprocessor and say:

#define  EV_DLL_2_NL                 EV_TIMER8
#define  raise_dll_2_nl_event(data)  CNET_start_timer(EV_DLL_2_NL, 1, data)

and then simply call raise_dll_2_nl_event() with an instance of CnetData when you want to raise the pseudo-event.

What is the meaning of "spelling mistake on line 83 of protocol.c"?

There is a spelling mistake on line 83 of protocol.c

What is the meaning of "caught signal number XX while (last) handling Perth.EV_APPLICATIONREADY"?

Old tricks for young players.

Fatal error messages of this form generally indicate a major problem with your protocols. The number (typically 2, 10 or 11) refers to an operating system signal number intercepted by cnet (see /usr/include/signal.h). For example, signal number 2 will be caught and reported by cnet if you interrupt cnet from the shell level (signal 2 = SIGINT). The other common signals, 10 and 11, reveal significant flaws in the implementation of your protocols.

Signal 10 (SIGBUS, a bus error) occurs when the CPU attempts to execute an instruction not on an instruction boundary (on many architectures, you've requested to execute an instruction whose address is not a multiple of 4). This error will generally occur when your programming corrupts your program's stack and, in particular, you corrupt the return address of the currently executing function. When the current function attempts to return (to a now incorrect address) and then fetches an instruction whose address is invalid, signal 10 will result.

Signal 11 (SIGSEGV, segmentation violation) occurs when your program attempts to address memory that has not been mapped into your address space. Typically, by accessing a pointer that has not been correctly initialized or has been modified/overwritten incorrectly, that pointer will point to memory that you do not "own", it being owned by either the operating system or another (person's) process. When attempting to access outside of your memory segment, you will get a segmentation violation. Operating systems that do not provide memory protection (segmentation), for example DOS, will not report this error as the (single) process on those operating systems "own" all of the address space. Your program there will still (maybe!) exhibit errors but these may not be reported to you. The operating system is in fact doing you a favour.

Signals 10 and 11 spell disaster for your programs - there is obviously something seriously wrong with your program if they happen. Both forms of error most frequently occur when you are incorrectly managing pointers and/or dynamic memory.

Such problems are very difficult to diagnose - your first action should be to check your programming logic. By their nature, errors which often *cause* signals 10 and 11 to be reported, do not necessarily raise the signal immediately. You may do the wrong thing many instructions or even seconds before the signal is reported. For this reason, the best cnet can do is state which event handler it was executing (or it was most recently executing) when the signal occurs. This does not necessarily indicate that your programming error is in that event handler though experience shows that this is likely.

What is the meaning of "warning: no more events are scheduled"?

This message means that cnet has nothing more to do (for your simulation) - all Application Layers are disabled and so no new messages will be generated, all frames have been delivered (or have been lost) via the Physical Layer, and all timers have expired or been cancelled. cnet has nothing further to do until you click a debug button, provide some keyboard input to one of the nodes, or terminate cnet.

What is the meaning of the statistic "Efficiency (bytes AL) / (bytes PL)"?

Here, AL stands for Application Layer, and PL for Physical Layer. This statistic divides the total number of bytes generated by all Application Layers, by the total number of bytes traversing the Physical Layer. Our protocols will require headers for their frames and packets, re-transmit data frames, and generate acknowledgments and other control packets, and so this ratio is expected to be less than 100% (the price we pay for reliable message delivery). The statistic is not updated until the first message is successfully written "up" to a node's Application Layer.

Under some circumstances this statistic appears to be inaccurate, and may not correspond exactly with your own calculations. This occurs when some bytes still "remain" in the Physical Layer and have yet to be delivered. Also, if your protocols perform compression of their payloads, then it is possible for this statistic to exceed 100%.

Keep in mind that this ratio is not the only desirable measure of protocol efficiency (but retains this name for historical reasons). Protocols may also strive to minimize average delivery time, or the total (monetary) cost of delivering frames.

What is the meaning of the error "ER_NOTREADY - Function called when service not available"?

This error arises in two common circumstances, usually when using functions to communicate with the Application or Physical layers:

  1. When a node is rebooting (its EV_REBOOT event handler is executing) data frames may not be written to the Physical Layer - it too needs to be rebooted before it can accept data. To overcome this limitation, set a timer for a very short interval, say just 1usec, and write to the Physical Layer in the timer's handler.

  2. cnet employs an event-driven paradigm to inform your protocol code that the Application and Physical layers require your attention. You may not poll either layer to see if new messages or data frames are available.

    Attempts to call CNET_read_application when not executing an EV_APPLICATIONREADY handler, or attempts to call CNET_read_physical when not executing an EV_PHYSICALREADY handler, will result in the functions failing and setting cnet_errno to ER_NOTREADY.

What is the meaning of the error "ER_TOOBUSY - Function is too busy/congested to handle request"?

The function CNET_write_physical() will 'trap' the situation when a large number of frames have been written to the Physical Layer, and when the receiving node has not read any of them off. This trap is currently set at the large value of 1000, which surely indicates an error in a protocol.

Your protocol may have some unbounded loop, or a very short timeout-and-retransmission sequence, resulting in many calls to CNET_write_physical() at the sender, before any EV_PHYSICALREADY events are handled at the receiver.

How can I speed up my simulations?

Firstly, it's important to keep some perspective. cnet runs within a single process on a single processor. If you dramatically increase the number of simulated nodes, expect a corresponding decrease in simulation speed. Even though the nodes appear to execute at the same time, their execution shares the single processor. However, all is not lost:

  1. Don't print out volumes of debug information to each node's output window. The printing of large amounts of text and the scrolling of these windows obviously slows down cnet. Just print out bad news, not lots of good news.

  2. Disable all printing, altogether - invoke cnet with the -q option to keep the windows quiet.

  3. cnet simulates multiple, independent, nodes by swapping their data segments (their variables) in and out of memory before each node's event handler is called. You can speed up your simulation (considerably) by reducing the space (number of bytes) required for each node's variables. Instead of declaring, say, a large buffer of 64KB as a global variable, declare a global pointer to such a buffer (probably requiring only 4 bytes) and in your reboot_node() handler, allocate the 64KB with a call to malloc(). Now, each time the node is scheduled, only the 4 byte pointer variable will be swapped in and out (and data allocated in the heap is not swapped).

  4. If your protocol works "for a few minutes" before crashing, change a few attributes to speed up the cnet simulation. For example:

        messagerate      = 3ms
        propagationdelay = 100usec

    should enable your protocol to work "for a few seconds" before it crashes. Hmmmm, much better.

  5. If you'd rather not wait a full second for cnet to complete one second of network activity, run with the -T option to force events to be scheduled immediately (nodeinfo.time_in_usec is updated as you'd hope).

  6. You don't always need to work with cnet's graphical display; it'll run "within" an ASCII terminal window without its display if you invoke cnet with its -W option. You can also use the -o option or explicitly send output to stdout, stderr or to a file rather than having it all appear in each node's stdout window.

    After a while, the gimmick of the "network map" should wear off and you should only be debugging bad news, ala by examining cnet_errno and CNET_perror().

How can I collate cnet statistics for plotting?

cnet centrally collates statistics on behalf of all nodes, and displays these on the 'Statistics' popup window or at the end of a simulation run if cnet is invoked with the -s option (or the -z option to also get zero-valued statistics).

We can also print statistics more frequently (periodically) with the correct choice of command line options. These are:

-W no need for the windowing interface
-T run the simulation as fast as possible
-e 5m execute for 5 minutes of simulation time
-s yes, we want statistics
-f 10s print statistics with a frequency (period) of 10 seconds

This will produce volumes of output to cnet's standard output stream, so we need to both capture this and probably filter only what we need. So, to capture the Efficiency measure (bytes AL/PL) every second (in the hope that it improves), we issue:

  cnet -W -T -e 5m -s -f 1s TOPOLOGYFILE   | \
  grep Efficiency                          | \
  cut -d: -f 2                             | \
  cat -n              > effic.statistics

The last line takes its input (a column of 300 efficiencies) and places a line number at the beginning of each line. This is fine if we really want statistics every single second, but slowly adapting protocols may take several minutes to reach their optimum. We could develop a shellscript which accepts arguments indicating the topology file and the frequency of collection:

  cnet -W -T -e 5m -s -f $FREQ $TOPFILE     | \
  grep Efficiency                           | \
  cut -d: -f 2                              | \
  awk '{ printf("%d %s\n", n+=freq, $0); }' freq=$FREQ  >  effic.statistics

Of course, other shellscript arguments could indicate the required statistic, resultfile, etc.

How can each node collate its own statistics?

Each node can easily collate statistics about its own performance in its own variables (in its C protocol source code). Each node can then use a timer event to periodically report its statistics (either accumulated values, or instantaneous values that are then cleared by the timer event handling code). If the statistics are printed in a consistent format, to C's stdout stream, then external shellscripts (as described above) may be written to further collate or plot information.

A node may set a handler for the EV_SHUTDOWN event, and then print its statistics when the node is politely shutdown, or when the whole simulation finishes.

Similarly, a node may set a handler for the EV_PERIODIC event, and then print its statistics whenever its handfler is called. The frequency with which the EV_PERIODIC event occurs may be requested with, for example:

    cnet -f 10secs TOPOLOGY

How can I collate my own global statistics?

Normally cnet provides the appearance that all nodes have independent memories (variables) so that all inter-node communication must be made via the Physical Layer. However, this feature can make the collation of global statistics difficult and, on such occasions, having access to a shared block of memory, say an array of integers indexed via each node's nodenumber, would be beneficial.

Consider the need to determine the number of re-transmissions required by each node executing a certain protocol - not a statistic that cnet can manage because it doesn't understand your code, and doesn't know what a re-transmission is. Let's assume that we have at most 100 nodes.

In each node's EV_REBOOT handler, we call CNET_shmem to request a shared memory region to store our re-transmission counts:

#include <cnet.h>

int *retransmissions;

void reboot_node(CnetEvent ev, CnetTimerID timer, CnetData data)
    retransmissions = (int *)CNET_shmem(100 * sizeof(int));

Then, when each node makes a re-transmission we can simply increment the relevent count in this shared array of integers:

    //  I have just re-transmitted a DATA frame
    ++retransmissions[ nodeinfo.nodenumber ];

All other nodes can instantly see the number of re-transmissions made by all other nodes, and any particular node could, say, print out all values when its EV_SHUTDOWN handler is called. The provision of CNET_shmem() is not intended to provide a covert communication channel between nodes.

 cnet v3.3.3, written by
 Last modified: Tue Oct 13 1:07PM 2015