HOWTO make and use a new application

From Nsnam
Jump to: navigation, search

Main Page - Current Development - Developer FAQ - Tools - Related Projects - Project Ideas - Summer Projects

Installation - Troubleshooting - User FAQ - HOWTOs - Samples - Models - Education - Contributed Code - Papers

One of the easiest ways a person can change the behavior of ns-3 is by writing an application.

In this HOWTO, we clone the existing UDP Echo application and associated example script. We change the client behavior by changing attributes in the new script; and we change server behavior by adding a new attribute to the server application and change how the server responds to requests. We use the new server attribute in the script. The resulting behavior (simulating a trivial request-response exchange) is verified in the pcap traces.

There are two basic approaches to building new things in ns-3:

1. You can work in the main ns-3 tree and add a new model exactly like the ns-3 developers would. This gives the flavor of a permanent change and will make it easier for you to submit your new application as an ns-3 contribution if you so desire in the future. This is the approach taken in this HOWTO.

2. Instead of making a more-or-less permanent change to the tree, you can work in the scratch directory. This is suitable for small efforts (such as writing a new main executable program) when you do not want to edit waf scripts.

HOWTO make and use a new application

1. Clone, build and test an ns-3 release or the ns development tree to use as a clean starting point. One way to do this is:

   a.  hg clone http://code.nsnam.org/ns-3-allinone
   b.  cd ns-3-allinone
   c.  ./download.py
   d.  ./build.py
   e.  cd ns-3-dev
   f.  ./test.py

2. Copy the UDP Echo applications into a new place to use as a starting point

   a.  cd src/applications/model
   b.  cp udp-echo-client.h request-response-client.h
   c.  cp udp-echo-client.cc request-response-client.cc
   d.  cp udp-echo-server.h request-response-server.h
   e.  cp udp-echo-server.cc request-response-server.cc

3. Change some strings and names to avoid conflicts (not all changes below will affect every file)

   a.  Edit (emacs or vi) the request-response-client.h file you just made
   b.  replace-string UdpEcho with RequestResponse
   c.  replace-string UDP_ECHO with REQUEST_RESPONSE
   d.  replace-string udp-echo with request-response
   e.  replace-string udpecho with requestresponse
   f.  replace-string Udp Echo with Request Response
   g.  save file
   h.  repeat for request-response-client.cc
   i.  repeat for request-response-server.h    
   j.  repeat for request-response-server.cc

4. Tell waf how to build the new applications

   a.  edit (emacs or vi) src/applications/wscript
   b.  where you find 'udp-echo-client.{cc,h}', copy the line and replace with 'request-response-client.{cc,h}'
   c.  repeat step b. for 'udp-echo-server'.  When you are done, you should have added four lines to the file.
   d.  cd ../../..

5. Build the new applications

   a.  ./waf build

6. Make a new helper for your new application

   a.  change directory into ns-3-dev/src/applications/helper
   b.  create a new helper from the existing UDP echo helper
       cp udp-echo-helper.cc request-response-helper.cc
       cp udp-echo-helper.h request-response-helper.h
   c.  Edit the new helper files and make the same substitutions as in step 3 
       above.
   d.  Edit again the src/applications/wscript file and where you find 'udp-echo-helper.{cc,h}', copy to 'request-response-helper.{cc,h}'.  This should add two new lines to the wscript.
   e.  cd ../../..

7. Build the new helper

   a.  ./waf

8. Make and build a new script to use the new application and helper

   a.  change directory into ns-3-dev/examples/udp/
   b.  cp udp-echo.cc request-response.cc
   c.  Edit the new script (request-response.cc) and make the same substitutions as in step 3 above.
   d.  Edit the wscript in this directory and add the new example to the build
       search for udp-echo
       copy the two lines of build instructions and rename udp-echo to request response in the copied lines.  
         obj = bld.create_ns3_program('request-response',
                                      ['csma', 'internet'])
         obj.source = 'request-response.cc'
   e.  save file
   f.  change into the top level directory (ns-3-dev)
       cd ../..
   g.  build the whole thing
       ./waf

9. Run your new script, application and helper

   a.  ./waf --run request-response

10. Observe new ASCII trace and pcap trace files in the top-level directory


Sidebar:

So far we've taken an existing application and done nothing to it but change names. Even after this minor change, we observed that the application worked exactly as expected. This is a generic strategy. Take as much existing code as possible and then make small changes, stopping to make sure it all still works as much as possible. Although this may seem silly at first, it allows you to get your feet wet in a large system without getting into a state where you have no idea what is wrong and no idea how to find and fix the problem. This is not a pleasant place to be.

So, I suggest that if you are going to make changes yourself, make just small incremental changes, and try to avoid pulling out code you think may be unneccesary. That code is there for a reason and you may not yet understand why. You might not understand all of the implications of pulling out a piece of code that other parts of the model might actually depend on. If you do pull out code, do it a little bit at a time, remember exactly what you just did, and run tests to make sure there were no undesirable effects. You then know exactly where to look for the source of a problem you may have just introduced.

If you are paranoid, cynical and completely untrusting of everything computational (like me) you may want to compare the output of your new request-response script to the original just to make 100% sure that everything is going according to plan.


You have now duplicated everything you need and can now get down to the business of making your new application do what you want. Let's just make a simple change to have the client send a small request packet to which the server replies with a big hunk of data.


1. Change the client application behavior

   a.  cd src/applications/model/
   b.  Edit up the request-response-client.cc file and take a look at the Attributes.
   c.  There's really nothing that needs to be done.  We can control the size of the 
       request packet directly with the "PacketSize" attribute.  We do that in the script
       not the application itself.
   d.  cd ../../../examples
   e.  open request-response.cc in your favorite editor
   f.  Search for "packetSize"
   g.  Change from 1024 to 10
   h.  Save file.

2. Change server application behavior to return some bigger packet

   a.  cd ../src/applications/model/
   b.  Edit request-response-client.cc
   c.  Copy the four lines of the .AddAttribute for the PacketSize to your editor's buffer
       .AddAttribute ("PacketSize", "Size of packets generated",
                      UintegerValue (100),
                      MakeUintegerAccessor (&RequestResponseClient::m_size),
                      MakeUintegerChecker<uint32_t> ())
   d.  Open request-response-server.cc
   e.  Yank the four lines into the GetTypeId method of the server (above the hanging semicolon).
   f.  Change the reference to RequestResponseClient in the added lines to RequestResponseServer.
   g.  Save the file.
   h.  You just added a reference to a variable RequestResponseServer_m_size, so you need to add it to the class.
       Open request-response-server.h
   i.  Find the declaration of m_port and add a declaration for m_size under it
       uint32_t m_size;
   j.  Save the file
   k.  Open request-response-server.cc
   l.  Search for RequestResponseServer::HandleRead
   m.  Look for the NS_LOG_LOGIC line and change the string "Echoing Packet" to something like "Responding"
   n.  You're going to need to change the next line of code
         socket->SendTo (packet, 0, from);
       to something reflecting the new m_size attribute, like
         Ptr<Packet> p = Create<Packet> (m_size);
         socket->SendTo (p, 0, from);
   o.  You should have something like,
       NS_LOG_LOGIC ("Responding");
       Ptr<Packet> p = Create<Packet> (m_size);
       socket->SendTo (p, 0, from);
   p.  Save file
   q.  Build the new application
       ./waf

3. Change script to use new server application behavior

   a.  from ns-3-dev, cd into examples/udp/
   b.  Edit request-response.cc
   c.  What you want to do now is to set the new server attribute to some large size (why does it default to 100 now?)
   d.  One way to do this is by command line (see the tutorial for how to do this)
   e.  Another way is in the script.
   f.  Find the RequestResponseServer declaration and add similar lines to those setting the packet size in the client app.
       uint32_t responseSize = 1024;
       RequestResponseServerHelper server (port);
       server.SetAttribute ("PacketSize", UintegerValue (responseSize));
   g.  You should have a server section that looks like,
         uint16_t port = 9;  // well-known echo port number
         RequestResponseServerHelper server (port);
         uint32_t responseSize = 1024;
         server.SetAttribute ("PacketSize", UintegerValue (responseSize));
         ApplicationContainer apps = server.Install (n.Get(1));
         apps.Start (Seconds (1.0));
         apps.Stop (Seconds (10.0));
   h.  Save file
   i.  Build the new example
       ./waf

3. Run un your new script with your new application behavior

   a.  cd ..
   b.  ./waf --run request-response

4. Observe the pcap traces to see what you've done

   a.  tcpdump -nn -tt -r request-response-0-1.pcap
         reading from file request-response-0-1.pcap, link-type EN10MB (Ethernet)
         2.000000 arp who-has 10.1.1.2 (ff:ff:ff:ff:ff:ff) tell 10.1.1.1
         2.004148 arp reply 10.1.1.2 is-at 00:00:00:00:00:02
         2.004148 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 10
         2.008312 arp who-has 10.1.1.1 (ff:ff:ff:ff:ff:ff) tell 10.1.1.2
         2.008312 arp reply 10.1.1.1 is-at 00:00:00:00:00:01
         2.014100 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
   b.  Note that a 10 byte request packet goes from 10.1.1.1 (node zero)
   c.  Note that a 1024 byte response packet comes back from 10.1.1.2

5. Go to Disneyland.


Craigdo 17:07, 29 May 2009 (UTC)