HOWTO make and use a new application

From Nsnam
Revision as of 17:07, 29 May 2009 by Craigdo (Talk | contribs) (HOWTO make and use a new application)

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. Editing files in a directory called scratch makes me personally very nervous so I basically don't do it. Calling a directory scatch means okay-to-delete-at-any-time to me. See the ns-3 tutorial (if you are a braver person than me) for how to use the scratch directory to make changes.

HOWTO make and use a new application

1. Clone, build and tests an ns-3 release to use as a clean starting point (this is actually out-of-date -- you should now use ns-3-allinone to accomplish the same thing)

   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.  ./waf check
   g.  ./waf --regression

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

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

3. Change the names to protect the innocent (not all changes below will affect every file)

   a.  Edit (emacs) 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) wscript
   b.  replace-string udp-echo with request-response
   c.  save file
   d.  cd ../..
   e.  edit ns-3-dev/src/wscript
   f.  find the string 'applications/udp-echo' in the all_modules assignment
   g.  copy that line to a new one and change udp-echo to request-response.  As in,
       'applications/udp-echo',
       'applications/request-response',

5. Build the new applications

   a.  cd ..
   b.  ./waf

6. Make a new helper for your new application

   a.  change directory into ns-3-dev/src/helper
       cd src/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 the helper wscript file and add a module dependency for your new helper file.
       Add the string "'request-response'," in the call to create_ns3_module, like,
         'udp-echo', 'request-response'])
   e.  Edit the helper wscript file and add the new files
       Add a line 'request-response-helper.cc', in the helper.source assignments.  Like
         'udp-echo-helper.cc',
         'request-response-helper.cc',
       Add a line, 'request-response-helper.h', in the headers.source assignments.  Like
         'udp-echo-helper.h',
         'request-response-helper.h',

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
       cd ../../examples
   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 and add the new example to the build
       search for udp-echo
       copy the three lines of build instructions and rename udp-echo to request response.  End up with three new lines
         obj = bld.create_ns3_program('request-response',
                                      ['csma', 'internet-stack'])
         obj.source = 'request-response.cc'
   e.  change into the top level directory (ns-3-dev)
       cd ..
   f.  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:

How you go about making changes to components in large systems like ns-3 can seriously affect the level of happiness and stress in your life. 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 completly 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/request-response/
   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.  
   d.  We do that in the script, so edit up ns-3-my-new-app/scratch/request-response.cc
   e.  Search for PacketSize
   f.  Change from 1024 to 10
   g.  Save file.

2. Change server application behavior to return some bigger packet

   a.  Edit up ns-3-my-new-app/src/applications/request-response/request-response-client.cc
   b.  Copy the three lines of the .AddAttribute for the PacketSize to your editors buffer
       .AddAttribute ("PacketSize", "Size of packets generated",
                      UintegerValue (100),
                      MakeUintegerAccessor (&RequestResponseClient::m_size),
                      MakeUintegerChecker<uint32_t> ())
   c.  Edit up ns-3-my-new-app/src/applications/request-response/request-response-server.cc
   d.  Yank the three lines into the GetTypeId method of the server (above the hanging semicolon).
   e.  Change the reference to RequestResponseClient in the new three lines to RequestResponseServer.
   f.  You just added a reference to a variable RequestResponseServer_m_size, so you need to add it to the class.
   g.  Edit up ns-3-my-new-app/src/applications/request-response/request-response-server.h
   h.  Find the declaration of m_port and add a declaration for m_size under it
       uint32_t m_size;
   i.  Edit up ns-3-my-new-app/src/applications/request-response/request-response-server.cc
   j.  Search for RequestResponseServer::HandleRead
   k.  Look for the NS_LOG_LOGIC line and change the string "Echoing Packet" to something like "Responding"
   l.  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.  The easiest way to get there is to steal the code from the client.
   m.  Edit up ns-3-my-new-app/src/applications/request-response/request-response-client.cc
   n.  Find RequestResponseClient::Send
   o.  Copy the following line to your editors buffer:
       Ptr<Packet> p = Create<Packet> (m_size);
   p.  Paste/Yank that line into the request-response-server.cc just uner the NS_LOG_LOGIC you just changed:
   q.  In the following line, change the reference from packet to p.  You should have the following lines of code
       NS_LOG_LOGIC ("Responding");
       Ptr<Packet> p = Create<Packet> (m_size);
       socket->SendTo (p, 0, from);

3. Change script to use new server application behavior

   a.  Edit up ns-3-my-new-app/scratch/request-response.cc
   b.  What you want to do now is to set the new server attribute to some large size (why does it default to 100 now?)
   c.  One way to do this is by command line (see the tutorial for how to do this)
   d.  Another way is in the script.
   e.  Find the ReqeustResponseServer 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));

3. Build and run your new script with your new application behavior

   a.  ./waf --run scratch/request-response

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

   a.  > tcpdump -nn -tt -r request-response-0-0.pcap
       reading from file request-response-0-0.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)