[翻译]NS下添加新协议

在这一部分,我将给处以个可以在ns上应用的新协议的例子。你应该在此之前相对熟悉ns,而且一些c++知识也是相对必须的。你也应该至少读过“ns注释和文档 ”(现在叫做手册)的3.1-3.3章节来理解Tcl和C++之间的交互。

这一部分的代码实现了简单的‘ping’协议(灵感来自“ns注释和文档 ”(现在叫做手册)的9.6章节,但是这个相对困难)。一个节点将会可以发送一个包到另一个节点,包还会快速的回传,这样来计算一次来回的时间。

我知道这里给出的代码可能不是最好的实现,我也确信它能够被改进,但是我希望它能够容易被理解,这就是这篇文章的主要目的。一些建议可以发送到:ns-users@isi.edu。

1.头文件

在一个新的头文件‘ping.h’中,我们首先需要说明新的ping包头部的数据结构,这将用来携带相关的数据。

1 struct hdr_ping {2   char ret;3   double send_time;4 };

字符类型‘ret’,如果包由来自发送方到被ping的节点时将会被设置为‘0’,在返回的路上,它将会被设置为‘1’。double类型的‘send_time’是一个时间戳,发送的时候设置,将会被用来计算来回的时间。

下面的代码声明了‘PingAgent’类,它是‘Agent’类的子类。

1 class PingAgent : public Agent {2  public:3   PingAgent();4   int command(int argc, const char*const* argv);5   void recv(Packet*, Handler*);6  protected:7   int off_ping_;  8 };

接下来的部分,我将会给出定义‘PingAgent()’/‘command()’和'recv()'的C++代码,这些在声明中都重定义。int型的‘off_ping_’将用来进入包的ping的头部。注意局部变量通常使用到'_'。

附(整体代码):

1 /* 2  * File: Header File for a new 'Ping' Agent Class for the ns 3  *       network simulator 4  * Author: Marc Greis (greis@cs.uni-bonn.de), May 1998 5  * 6  */ 7  8  9 #ifndef ns_ping_h10  #define ns_ping_h11 12 #include "agent.h"13 #include "tclcl.h"14 #include "packet.h"15 #include "address.h"16 #include "ip.h"17 18 19  struct hdr_ping {20   char ret;21   double send_time;22 };23 24 25  class PingAgent : public Agent {26  public:27   PingAgent();28   int command(int argc, const char*const* argv);29   void recv(Packet*, Handler*);30  protected:31   int off_ping_;32 };33 34 35  #endif

2.C++代码

首先,C++和Tcl代码之间的连接需要被定义。你不需要完全理解这些代码,但是如果还没有理解的话可以去阅读“ns手册”3.1-3.3节。

1 static class PingHeaderClass : public PacketHeaderClass { 2  public: 3   PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping", sizeof(hdr_ping)) {} 5 } class_pinghdr; 6  7  8  static class PingClass : public TclClass { 9  public:10   PingClass() : TclClass("Agent/Ping") {}11   TclObject* create(int, const char*const*) {12     return (new PingAgent());13   }14 } class_ping;

接下来的代码段构造了‘PingAgent’类。它连接了需要在Tcl和C++中使用的变量。

1 PingAgent::PingAgent() : Agent(PT_PING)2 {3   bind("packetSize_", &size_);4   bind("off_ping_", &off_ping_);5 }

‘Command()’函数在对于'PingAgent'类的Tcl命令执行时被执行。在我们的例子中,这就是‘$pa send’(假定‘pa’是一个Agent/Ping类的实例),因为我们想从Agent到其他Agent发送ping包。你最起码应该将这些命令放入‘command()’函数中,如果没有找到这个匹配,你不得不连同参数将命令传递到基类的‘command()’(在这里就是‘Agent::command()’)。因为它的注释比较多,代码可能看起来比较长。

1 int PingAgent::command(int argc, const char*const* argv) 2 { 3   if (argc == 2) { 4     if (strcmp(argv[1], "send") == 0) { 5       // Create a new packet 6        Packet* pkt = allocpkt(); 7       // Access the Ping header for the new packet: 8        hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_); 9       // Set the 'ret' field to 0, so the receiving node knows10       // that it has to generate an echo packet11        hdr->ret = 0;12       // Store the current time in the 'send_time' field13        hdr->send_time = Scheduler::instance().clock();14       // Send the packet15        send(pkt, 0);16       // return TCL_OK, so the calling function knows that the17       // command has been processed18        return (TCL_OK);19     }20   }21   // If the command hasn't been processed by PingAgent()::command,22   // call the command() function for the base class23    return (Agent::command(argc, argv));24 }

‘recv()’函数定义了接收包时的动作。如果‘ret’是0,包的‘send_time’值不变,但是‘ret’设为1,返回包。如果‘ret’为1,引发一个Tcl函数(需要用户在Tcl中定义)。

1 void PingAgent::recv(Packet* pkt, Handler*) 2 { 3   // Access the IP header for the received packet: 4    hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_); 5   // Access the Ping header for the received packet: 6    hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_); 7   // Is the 'ret' field = 0 (i.e. the receiving node is being pinged)? 8    if (hdr->ret == 0) { 9     // Send an 'echo'. First save the old packet's send_time10      double stime = hdr->send_time;11     // Discard the packet12      Packet::free(pkt);13     // Create a new packet14      Packet* pktret = allocpkt();    15     // Access the Ping header for the new packet:16      hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);17     // Set the 'ret' field to 1, so the receiver won't send another echo18      hdrret->ret = 1;                19     // Set the send_time field to the correct value20      hdrret->send_time = stime;      21     // Send the packet              22      send(pktret, 0);                23   } else {                          24     // A packet was received. Use tcl.eval to call the Tcl25     // interpreter with the ping results.26     // Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'27     // has to be defined which allows the user to react to the ping28     // result.                      29      char out[100];                  30     // Prepare the output to the Tcl interpreter. Calculate the round31     // trip time                    32      sprintf(out, "%s recv %d %3.1f", name(), 33             hdrip->src_.addr_ >> Address::instance().NodeShift_[1],34             (Scheduler::instance().clock()-hdr->send_time) * 1000);35     Tcl& tcl = Tcl::instance();     36     tcl.eval(out);                  37     // Discard the packet           38      Packet::free(pkt);              39   }                                 40 }

下面将给出整体的文件。最有意思的部分应该是‘tcl.eval()’函数,这里‘recv’函数被调用,这里使用被ping节点的ID号和来回时间(毫秒)作为参数。第4部分将会给出如何编写此类功能。但是首先,一些其他的文件需要先被编写,以确保ns可以编译通过。

附(整体代码):

1 /*  2  * File: Code for a new 'Ping' Agent Class for the ns  3  *       network simulator  4  * Author: Marc Greis (greis@cs.uni-bonn.de), May 1998  5  *  6  */  7   8   9 #include "ping.h" 10  11  12  static class PingHeaderClass : public PacketHeaderClass { 13  public: 14   PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping",  15                     sizeof(hdr_ping)) {} 16 } class_pinghdr; 17  18  19  static class PingClass : public TclClass { 20  public: 21   PingClass() : TclClass("Agent/Ping") {} 22   TclObject* create(int, const char*const*) { 23     return (new PingAgent()); 24   } 25 } class_ping; 26  27  28 PingAgent::PingAgent() : Agent(PT_PING) 29 { 30   bind("packetSize_", &size_); 31   bind("off_ping_", &off_ping_); 32 } 33  34  35  int PingAgent::command(int argc, const char*const* argv) 36 { 37   if (argc == 2) { 38     if (strcmp(argv[1], "send") == 0) { 39       // Create a new packet 40        Packet* pkt = allocpkt(); 41       // Access the Ping header for the new packet: 42        hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_); 43       // Set the 'ret' field to 0, so the receiving node knows 44       // that it has to generate an echo packet 45        hdr->ret = 0; 46       // Store the current time in the 'send_time' field 47        hdr->send_time = Scheduler::instance().clock(); 48       // Send the packet 49       send(pkt, 0); 50       // return TCL_OK, so the calling function knows that the 51       // command has been processed 52       return (TCL_OK); 53     } 54   } 55   // If the command hasn't been processed by PingAgent()::command, 56   // call the command() function for the base class 57   return (Agent::command(argc, argv)); 58 } 59  60  61 void PingAgent::recv(Packet* pkt, Handler*) 62 { 63   // Access the IP header for the received packet: 64   hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_); 65   // Access the Ping header for the received packet: 66   hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_); 67   // Is the 'ret' field = 0 (i.e. the receiving node is being pinged)? 68   if (hdr->ret == 0) { 69     // Send an 'echo'. First save the old packet's send_time 70     double stime = hdr->send_time; 71     // Discard the packet 72     Packet::free(pkt); 73     // Create a new packet 74     Packet* pktret = allocpkt(); 75     // Access the Ping header for the new packet: 76     hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_); 77     // Set the 'ret' field to 1, so the receiver won't send another echo 78     hdrret->ret = 1; 79     // Set the send_time field to the correct value 80     hdrret->send_time = stime; 81     // Send the packet 82     send(pktret, 0); 83   } else { 84     // A packet was received. Use tcl.eval to call the Tcl 85     // interpreter with the ping results. 86     // Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}' 87     // has to be defined which allows the user to react to the ping 88     // result. 89     char out[100]; 90     // Prepare the output to the Tcl interpreter. Calculate the round 91     // trip time 92     sprintf(out, "%s recv %d %3.1f", name(),  93             hdrip->src_ >> Address::instance().NodeShift_[1],  94         (Scheduler::instance().clock()-hdr->send_time) * 1000); 95     Tcl& tcl = Tcl::instance(); 96     tcl.eval(out); 97     // Discard the packet 98     Packet::free(pkt); 99   }100 }

3.必要的改变

如果你想添加一个新协议的话,你将会需要改变一些ns源文件,尤其是如果它使用了一个新的包格式。我建议你对这些改变做上注释,使用#ifdef等等,这样的话你就能够容易移除你的改变或者移植到心的ns版本上。

我们接下来需要为ping agent弄一个新的包类型,所以第一步是编辑文件‘packet.h’。那里,你可以发现包协议的ID的定义(比如PT_TCP,PT_Telnet等等)。为PT_PING在那里添加一个新的定义。在我编辑的packet.h版本中,enum packet_t{}的最后几行是下面的代码(它也许看起来和之前或者以后的版本有些不同)。

1 enum packet_t { 2     PT_TCP, 3     PT_UDP, 4         ...... 5     // insert new packet types here 6     PT_TFRC, 7     PT_TFRC_ACK, 8         PT_PING,    //  packet protocol ID for our ping-agent 9     PT_NTYPE // This MUST be the LAST one10 };

你也需要在这个文件里编辑p_info()来增加“Ping”。

1 class p_info { 2 public: 3     p_info() { 4         name_[PT_TCP]= "tcp"; 5         name_[PT_UDP]= "udp"; 6                 ........... 7          name_[PT_TFRC]= "tcpFriend"; 8         name_[PT_TFRC_ACK]= "tcpFriendCtl"; 9 10                 name_[PT_PING]="Ping";11 12         name_[PT_NTYPE]= "undefined";13     }14         .....15  }

记住,在‘make’之前你需要做一个‘make depend’,否则这两个文件不会再编译。

文件‘tcl/lib/ns-default.tcl’也需要被编辑。这个文件定义了所有定义的Tcl类的缺省值。添加下面几行来设置Agent/Ping包的缺省大小。

1 Agent/Ping set packetSize_ 64

你也需要在文件‘tcl/lib/ns-packet.tcl’添加新ping包的入口,在文件开始的列表中。它是以下的代码。

1         { SRMEXT off_srm_ext_}2         { Ping off_ping_ }} {3 set cl PacketHeader/[lindex $pair 0]

最后的改变是‘Makefile’。你需要添加文件‘ping.o’到类文件列表。在我的版本中,最后几行改成如下:

1 sessionhelper.o delaymodel.o srm-ssm.o \2 srm-topo.o \3 ping.o \4 $(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o \5 $(LIB_DIR)dmalloc_support.o \

你现在可以重编译ns,只需要在ns目录中打入‘make’。如果你有问题,请电邮作者。

4.Tcl代码

下面写出了‘recv’过程(在C++中‘recv()’函数中调用,在ping包被接收时):

1 Agent/Ping instproc recv {from rtt} {2         $self instvar node_3         puts "node [$node_ id] received ping answer from \4               $from with round-trip-time $rtt ms."5 }

这个代码应该比较容易理解。唯一的新的东西是它使用了基类‘Agent’的变量‘node_’来获取agent依附的节点的节点ID。

附(完全代码):

1 #Create a simulator object 2 set ns [new Simulator] 3  4 #Open a trace file 5 set nf [open out.nam w] 6 $ns namtrace-all $nf 7  8 #Define a 'finish' procedure 9 proc finish {} {10         global ns nf11         $ns flush-trace12         close $nf13         exec nam out.nam &14         exit 015 }16 17 #Create three nodes18 set n0 [$ns node]19 set n1 [$ns node]20 set n2 [$ns node]21 22 #Connect the nodes with two links23 $ns duplex-link $n0 $n1 1Mb 10ms DropTail24 $ns duplex-link $n1 $n2 1Mb 10ms DropTail25 26 #Define a 'recv' function for the class 'Agent/Ping'27 Agent/Ping instproc recv {from rtt} {28     $self instvar node_29     puts "node [$node_ id] received ping answer from \30               $from with round-trip-time $rtt ms."31 }32 33 #Create two ping agents and attach them to the nodes n0 and n234 set p0 [new Agent/Ping]35 $ns attach-agent $n0 $p036 37 set p1 [new Agent/Ping]38 $ns attach-agent $n2 $p139 40 #Connect the two agents41 $ns connect $p0 $p142 43 #Schedule events44 $ns at 0.2 "$p0 send"45 $ns at 0.4 "$p1 send"46 $ns at 0.6 "$p0 send"47 $ns at 0.6 "$p1 send"48 $ns at 1.0 "finish"49 50 #Run the simulation51 $ns run

现在你可以试着做一些实验。Good Luck!
原文链接: https://www.cnblogs.com/program_thinker/archive/2011/03/24/1993426.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/22761

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 上午12:45
下一篇 2023年2月8日 上午12:45

相关推荐