[翻译]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_h
10  #define ns_ping_h
11
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 knows
10 // that it has to generate an echo packet
11   hdr->ret = 0;
12 // Store the current time in the 'send_time' field
13   hdr->send_time = Scheduler::instance().clock();
14 // Send the packet
15   send(pkt, 0);
16 // return TCL_OK, so the calling function knows that the
17 // command has been processed
18   return (TCL_OK);
19 }
20 }
21 // If the command hasn't been processed by PingAgent()::command,
22 // call the command() function for the base class
23   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_time
10   double stime = hdr->send_time;
11 // Discard the packet
12   Packet::free(pkt);
13 // Create a new packet
14   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 echo
18   hdrret->ret = 1;
19 // Set the send_time field to the correct value
20   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 Tcl
25 // 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 ping
28 // result.
29   char out[100];
30 // Prepare the output to the Tcl interpreter. Calculate the round
31 // 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 one
10 };

你也需要在这个文件里编辑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 nf
11 $ns flush-trace
12 close $nf
13 exec nam out.nam &
14 exit 0
15 }
16
17 #Create three nodes
18 set n0 [$ns node]
19 set n1 [$ns node]
20 set n2 [$ns node]
21
22 #Connect the nodes with two links
23 $ns duplex-link $n0 $n1 1Mb 10ms DropTail
24 $ns duplex-link $n1 $n2 1Mb 10ms DropTail
25
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 n2
34 set p0 [new Agent/Ping]
35 $ns attach-agent $n0 $p0
36
37 set p1 [new Agent/Ping]
38 $ns attach-agent $n2 $p1
39
40 #Connect the two agents
41 $ns connect $p0 $p1
42
43 #Schedule events
44 $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 simulation
51 $ns run

  现在你可以试着做一些实验。Good Luck!

posted on 2011-09-04 14:37  心月  阅读(1180)  评论(0编辑  收藏  举报