close
Warning:
BrowserModule failed with ConfigurationError: Look in the Trac log for more information.
- Timestamp:
-
Apr 28, 2009, 12:35:31 PM (16 years ago)
- Author:
-
heep
- Comment:
-
--
Legend:
- Unmodified
- Added
- Removed
- Modified
-
v18
|
v19
|
|
35 | 35 | For module declarations, !OverSim uses the NED language, a topology description language. Modules should be declared in their own NED files (with extension .ned), and the file name should match the module name (minus the extension!). |
36 | 36 | |
37 | | First, we start with our overlay module. All overlay module files should be located in <OverSim>/src/overlay, so we'll create a folder called myoverlay, and inside, we'll create a file called MyOverlay.ned. |
38 | | |
39 | | Following is the declaration for MyOverlay: |
| 37 | First, we start with our overlay module. All overlay module files should be located in !OverSim/src/overlay, so we'll create a folder called myoverlay, and inside, we'll create a file called !MyOverlay.ned. |
| 38 | |
| 39 | Following is the declaration for !MyOverlay: |
40 | 40 | |
41 | 41 | {{{ |
… |
… |
|
58 | 58 | The parameters subsection is a list of module properties. These parameters must be set before the simulation (or the simulator will stop and ask you for a value each time a module is created!) and will be read-only while the simulation is running. As parameter types we can use booleans (bool), strings, integers, or doubles; custom types (either module declarations or C++ types) can also be used. |
59 | 59 | |
60 | | Notice that, since we're writing an overlay module, we'll declare our module as inheriting from the parent class BaseOverlay. Therefore we'll use "extends BaseOverlay" in the module definition. Since we want to use our own code for our module, we'll add "@class(MyOverlay)" in the parameters section, to tell the simulator that MyOverlay is a class of its own. |
61 | | |
62 | | Now, we'll declare our application module in a similar way. Application files should be located in <OverSim>/src/applications, so we'll create a folder called myapplication (folder names are in lower case by convention) and there a file called MyApplication.ned. |
| 60 | Notice that, since we're writing an overlay module, we'll declare our module as inheriting from the parent class BaseOverlay. Therefore we'll use "extends BaseOverlay" in the module definition. Since we want to use our own code for our module, we'll add "@class(!MyOverlay)" in the parameters section, to tell the simulator that !MyOverlay is a class of its own. |
| 61 | |
| 62 | Now, we'll declare our application module in a similar way. Application files should be located in !OverSim/src/applications, so we'll create a folder called !myapplication (folder names are in lower case by convention) and there a file called !MyApplication.ned. |
63 | 63 | |
64 | 64 | {{{ |
… |
… |
|
85 | 85 | === 2.1.1 Simple and Compound Modules === |
86 | 86 | |
87 | | MyOverlay and MyApplication are examples of what is called a 'simple' module, because they don't contain any other modules inside them. Simple modules are declared using the keyword "simple" (for example, "simple MyOverlay"). Only simple modules can have its behaviour customized with C++ (see Section 4). |
| 87 | !MyOverlay and !MyApplication are examples of what is called a 'simple' module, because they don't contain any other modules inside them. Simple modules are declared using the keyword "simple" (for example, "simple !MyOverlay"). Only simple modules can have its behaviour customized with C++ (see Section 4). |
88 | 88 | |
89 | 89 | On the other hand, modules with inner nested modules are called compound modules, and act only as containers. They are declared with the keyword "module". These are useful when we want to organize all the modules related to your overlay in a single place. |
… |
… |
|
109 | 109 | |
110 | 110 | connections allowunconnected: |
111 | | udpIn --> myOverlay.udpIn++; |
112 | | udpOut <-- myOverlay.udpOut++; |
| 111 | udpIn --> myOverlay.udpIn; |
| 112 | udpOut <-- myOverlay.udpOut; |
113 | 113 | appIn --> myOverlay.appIn; |
114 | 114 | appOut <-- myOverlay.appOut; |
… |
… |
|
116 | 116 | }}} |
117 | 117 | |
118 | | As you can see, the inner module "myOverlay" of type MyOverlay is nested inside MyOverlayModules. However, when we use MyOverlayModules, all messages that need to be sent to myOverlay will be sent to the wrapper instead. Therefore, we need to set up which gates we're going to use (input and output to the overlay and the UDP module), and then connect them to the overlay gates. There is no need to change the default entries in the sections "gates" and "connections" for other overlays (but keep the order! gates - submodules - connections). |
| 118 | As you can see, the inner module "myOverlay" of type !MyOverlay is nested inside !MyOverlayModules. However, when we use !MyOverlayModules, all messages that need to be sent to myOverlay will be sent to the wrapper instead. Therefore, we need to set up which gates we're going to use (input and output to the overlay and the UDP module), and then connect them to the overlay gates. There is no need to change the default entries in the sections "gates" and "connections" for other overlays (but keep the order! gates - submodules - connections). |
119 | 119 | |
120 | 120 | Notice that we used "like" instead of "extends". This is because IOverlay is simply an interface, not a parent class; we don't want to inherit from it. Additionaly, no "@class" is needed, since compound modules can't be extended with C++ code. |
… |
… |
|
153 | 153 | == 3. Messages == |
154 | 154 | |
155 | | For sending messages, we can use the basic cMessage class. However, we'll be needing a few custom fields for our application, so we'll use a customized message instead. Custom message declarations are like those of modules, but without sections: simply include the parameters directly. The framework will automatically create getter- and setter-functions for each of them. |
| 155 | For sending messages, we can use the basic cPacket class (which inherits from the generic cMessage class). However, we'll be needing a few custom fields for our application. Thus, we'll use a customized message instead. Custom message declarations are like those of modules, but without sections: simply include the parameters directly. The framework will automatically create getter- and setter-functions for each of them. |
156 | 156 | |
157 | 157 | And like modules, messages should be declared in their own (.msg) files. |
158 | 158 | |
159 | | In this case, let's see MyMessage.msg: |
| 159 | In this case, let's see !MyMessage.msg: |
160 | 160 | |
161 | 161 | {{{ |
… |
… |
|
186 | 186 | In order to route packets in a realistic way, the "length" parameter of the message should be set to an appropiate value. This is done manually by using the setBitLength(bits) or setByteLength(bytes) function when sending the message. Forgetting to set this value will default the length to a size of 0, potentially making measurements such as latencies less meaningful. |
187 | 187 | |
188 | | All messages can encapsulate any other message (just avoid recursivity!) by using the encapsulate() function. To decapsulate the message, use decapsulate(). To peek the encapsulated message without decapsulating it, use getEncapsulatedMsg(). The length of the message is automatically changed accordingly. |
189 | | |
190 | | Additionaly, each message that is created must be deleted exactly once. Messages that aren't deleted will cause memory leaks, and those that are deleted twice can cause a segmentation fault. It is the responsability of the programmer to know when a message should be deleted. |
| 188 | All messages can encapsulate any other message (just avoid recursivity!) by using the encapsulate() function. To decapsulate the message, use decapsulate(). To peek the encapsulated message without decapsulating it, use getEncapsulatedMsg(). The length of the message is automatically updated. |
| 189 | |
| 190 | Additionaly, each message that is created must be deleted exactly once. Messages that aren't deleted will cause memory leaks, and those that are deleted twice can cause a segmentation fault. It is the responsibility of the programmer to know when a message should be deleted. |
191 | 191 | |
192 | 192 | == 4. Implementing the overlay and application modules == |
… |
… |
|
210 | 210 | IPvXAddress: Generic IP address, can contain either an IPv4 address (IPAddress) or an IPv6 address (IPv6Address).[[BR]] |
211 | 211 | TransportAddress: A structure containing bot an IPvXAddress (field "ip"), and a port (field "port").[[BR]] |
212 | | NodeHandle: A child class of TransportAddress, additionaly contains an OverlayKey (field "key"). In overlay modules, the NodeHandle value representing the node in which the module is can be retrieved with "thisNode". In application modules, thisNode also exists, but the "key" field may not be set![[BR]] |
| 212 | NodeHandle: A child class of TransportAddress, additionaly contains an OverlayKey (field "key"). In overlay and application modules, the variable "thisNode" contains the NodeHandle value representing the host. However, in application modules the "key" field may not be set.[[BR]] |
213 | 213 | |
214 | 214 | Now, let's continue with the implementation. |
… |
… |
|
216 | 216 | == 4.1 Implementing the Application == |
217 | 217 | |
218 | | First, we'll start with MyApplication, since the application interface is easier to understand. Our application will implement a simple PING-type program, in which a packet is sent to another node, and that node sends the packet back. When we start, we'll wait <sendPeriod> seconds, then we'll route <numToSend> packets to random keys through the overlay. When a node receives a packet, it will send it back to the owner through UDP. The process is repeated until the simulation ends. |
219 | | |
220 | | This is the declaration of MyApplication from MyApplication.h: |
| 218 | First, we'll start with !MyApplication, since the application interface is easier to understand. Our application will implement a simple PING-type program, in which a packet is sent to another node, and that node sends the packet back. When we start, we'll wait <sendPeriod> seconds, then we'll route <numToSend> packets to random keys through the overlay. When a node receives a packet, it will send it back to the owner through UDP. The process is repeated until the simulation ends. |
| 219 | |
| 220 | If you use UDP and expect to receive packets, notice that you first must bind a port for your application to use. This can be done with bindToPort(port), and you can bind as many ports as you like. Note however that, when receiving a packet, the port in which the packet came won't be passed up to the application. Don't use port numbers 1024-1034, as they are reserved for overlay use. The overlay module reserves its ports automatically. |
| 221 | |
| 222 | This is the declaration of !MyApplication from !MyApplication.h: |
221 | 223 | |
222 | 224 | {{{ |
… |
… |
|
245 | 247 | }}} |
246 | 248 | |
247 | | Now comes the implementation of MyApplication from MyApplication.cc: |
| 249 | Now comes the implementation of !MyApplication from !MyApplication.cc: |
248 | 250 | |
249 | 251 | {{{ |
… |
… |
|
284 | 286 | timerMsg = new cMessage("MyApplication Timer"); |
285 | 287 | scheduleAt(simTime() + sendPeriod, timerMsg); |
| 288 | |
| 289 | // bind our port to receive UDP packets |
| 290 | // the value for thisNode.port will be updated accordingly |
| 291 | bindToPort(2048); |
286 | 292 | } |
287 | 293 | |
… |
… |
|
310 | 316 | { |
311 | 317 | if (msg == timerMsg) { // is this our timer? |
312 | | |
313 | 318 | scheduleAt(simTime() + sendPeriod, timerMsg); // reschedule our message |
314 | 319 | |
… |
… |
|
330 | 335 | } |
331 | 336 | } else { |
332 | | delete msg; // who knows what this packet is? |
| 337 | delete msg; // unknown packet |
333 | 338 | } |
334 | 339 | } |
… |
… |
|
340 | 345 | { |
341 | 346 | // we are only expecting messages of type MyMessage, throw away any other |
342 | | |
343 | 347 | MyMessage *myMsg = dynamic_cast<MyMessage*>(msg); |
344 | 348 | if (myMsg == 0) { |
… |
… |
|
348 | 352 | |
349 | 353 | // are we a PING? send a PONG! |
350 | | |
351 | 354 | if (myMsg->getType() == MYMSG_PING) { |
352 | | |
353 | 355 | myMsg->setType(MYMSG_PONG); // change type |
354 | 356 | sendMessageToUDP(myMsg->getSenderAddress(), myMsg); // send it back to its owner |
… |
… |
|
359 | 361 | |
360 | 362 | // handleUDPMessage is called when we receive a message from UDP. |
| 363 | // Parameter msg is actually of type cPacket*, set to the more generic cMessage* for legacy purposes. |
361 | 364 | // Unhandled or unknown packets can be safely deleted here. |
362 | 365 | |
… |
… |
|
364 | 367 | { |
365 | 368 | // we are only expecting messages of type MyMessage |
366 | | |
367 | 369 | MyMessage *myMsg = dynamic_cast<MyMessage*>(msg); |
368 | 370 | |
… |
… |
|
372 | 374 | |
373 | 375 | // Whatever msg was, we won't need it anymore. |
374 | | |
375 | 376 | delete msg; |
376 | 377 | } |
… |
… |
|
382 | 383 | Now, we'll implement our overlay module. Our overlay will be rather simple, and based on only two concepts. First, an overlay key maps directly to the IP address of its holder (for example, key 16 will be held by 1.0.0.16). And second, routing a packet from key A to key B will consist of sending that packet to all nodes with keys between A and B (for example, sending a node from key 4 to key 1 will create a route of 4 - 3 - 2 - 1). |
383 | 384 | |
384 | | This is the declaration of MyOverlay in MyOverlay.h |
| 385 | This is the declaration of !MyOverlay in !MyOverlay.h |
385 | 386 | |
386 | 387 | {{{ |
… |
… |
|
407 | 408 | // obligatory: called when we need the next hop to route a packet to the given key |
408 | 409 | NodeVector* findNode(const OverlayKey& key, // key to route to |
409 | | int numRedundantNodes, // how many candidates for next hop we want (see getMaxNumSiblings) |
410 | | int numSiblings, // how many siblings we'll return (?) (see getMaxRedundantNodes) |
411 | | BaseOverlayMessage* msg); // message being routed |
412 | | |
413 | | // obligatory: In general, called when we need to know whether node is amongst numSiblings closest nodes to key. |
414 | | // But normally it is called with node set to this node, and asking whether this node is responsible for key. |
| 410 | int numRedundantNodes, // next hop candidates to return if we're not responsible for "key" |
| 411 | int numSiblings, // how many siblings to return if we're responsible for "key" |
| 412 | BaseOverlayMessage* msg); // message being routed |
| 413 | |
| 414 | // obligatory: In general, called when we need to know whether "node" is amongst numSiblings closest nodes to "key". |
| 415 | // But normally it is called with "node" set to "thisNode", and asking whether we are responsible for "key" |
415 | 416 | bool isSiblingFor(const NodeHandle& node, // which node (usually thisNode) we're referring to |
416 | 417 | const OverlayKey& key, // key in question |
… |
… |
|
418 | 419 | bool* err); // set to false when we couldn't determine the range |
419 | 420 | |
420 | | // obligatory: Set the maximum number of siblings that can be queried about (usually 1) |
| 421 | // obligatory: Set the maximum number of siblings that can be queried about in isSiblingFor(usually 1) |
421 | 422 | int getMaxNumSiblings(); |
422 | 423 | |
423 | | // obligatory: Set the maximum number of redundant nodes that can be queried about (usually 1) |
| 424 | // obligatory: Set the maximum number of redundant nodes that can be queried about in isSiblingFor (usually 1) |
424 | 425 | int getMaxNumRedundantNodes(); |
425 | 426 | |
… |
… |
|
427 | 428 | }}} |
428 | 429 | |
429 | | Now comes the implementation of MyOverlay from MyOverlay.cc: |
| 430 | Now comes the implementation of !MyOverlay from !MyOverlay.cc: |
430 | 431 | |
431 | 432 | {{{ |
… |
… |
|
502 | 503 | } |
503 | 504 | |
504 | | nextHops = new NodeVector(1); // else, set the response vector with one node |
| 505 | // numRedundantNodes and numSiblings are both maximal 1 |
| 506 | nextHops = new NodeVector(1); |
505 | 507 | |
506 | 508 | if (key == thisNode.key) { // are we responsible? next step is this node |
… |
… |
|
550 | 552 | }}} |
551 | 553 | |
552 | | In this example, we are working with the network SimpleOverlay. From there, we need node number 5, and then its overlay module. For that module, we'll use the submodule myOverlay, and set the parameter enableDrops to true. |
| 554 | In this example, we are working with the network SimpleOverlay. From there, we retrieve the overlayTerminal array, get index 5, and then its overlay module. For that module, we'll use the submodule myOverlay, and set the parameter enableDrops to true. |
553 | 555 | |
554 | 556 | This case, however, is too specific. We may not always work with the SimpleOverlay network. Or we may need that parameter to be set for all nodes. For those cases the wildcards * and ** are of use. For example: |
… |
… |
|
563 | 565 | |
564 | 566 | Should a module parameter not be set in either configuration file, or match any wildcard, !OverSim will prompt the user to enter a value for each instance of the module. For simulations with a big amount of nodes, setting each parameter individually quickly becomes overwhelming. Therefore, it is recommended that every module parameter be assigned a default value in default.ini. |
| 567 | |
| 568 | Notice that these are parameter paths, not to be confused with module paths from NED declarations (see Section 2.1). In this case, the parameter paths describe a set of modules and submodules to follow to reach the inner parameter we want. On the other hand, the module paths describe the folder under <OverSim>/src/ in which the declaration of the NED module will be found. |
565 | 569 | |
566 | 570 | == 5.2 Creating our Configuration File == |
… |
… |
|
595 | 599 | The churn type tells OverSim how often it should add or delete nodes from the network. There are many churn types (e.g. NoChurn for a static network, LifetimeChurn for creating nodes with a given lifetime distribution, and so on); for a complete list see the ned files in <OverSim>/src/common. In our example, we'll use the less interesting NoChurn, which disables churn in our network. There are plenty of examples in <OverSim>/simulations/omnetpp.ini for all churns. |
596 | 600 | |
597 | | The second part are the node parameters. Two node parameters are obligatory: tier1Type (to specify the application module), and overlayType (for the overlay module). Further parameters can be specified for these application and overlay modules. For this example, we'll use the module MyOverlayModules, which we declared in a previous chapter. |
| 601 | The second part are the node parameters. Two node parameters are obligatory: tier1Type (to specify the application module), and overlayType (for the overlay module). Further parameters can be specified for these application and overlay modules. For this example, we'll use the module !MyOverlayModules, which we declared in a previous chapter. |
598 | 602 | |
599 | 603 | {{{ |
… |
… |
|
696 | 700 | == 7. Remote Procedure Calls == |
697 | 701 | |
698 | | Remote Procedure Calls (RPCs) are a useful paradigm for when an application wants to execute a function, but where the context and / or the code are in another host, yet in a transparent way. Continuing our MyOverlay example, let's create an RPC function that retrieves the neighbors from another node. |
699 | | |
700 | | The MyOverlay header would look this way: |
| 702 | Remote Procedure Calls (RPCs) are a useful paradigm for when an application wants to execute a function, but where the context and / or the code are in another host, yet in a transparent way. Continuing our !MyOverlay example, let's create an RPC function that retrieves the neighbors from another node. |
| 703 | |
| 704 | The !MyOverlay header would look this way: |
701 | 705 | |
702 | 706 | {{{ |
… |
… |
|
869 | 873 | |
870 | 874 | == Have fun! == |
871 | | |