在这一部分,我将给处以个可以在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
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!