zoukankan      html  css  js  c++  java
  • [翻译]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!

  • 相关阅读:
    VLAN
    Debug出错 Release没问题
    弹出VIEW.非dialog
    [转帖]Android Bitmap内存限制OOM,Out Of Memory
    关于性格内向者的10个误解,献给奋战在一线的程序员
    Android 调用webservice
    Android2.2新特性.APK安装参数installLocation
    数据结构中的基本排序算法总结
    Post Operation for Windows 8 Apps || Windows Phone
    ZipArchive For Windows 8 Apps
  • 原文地址:https://www.cnblogs.com/zhangleiccst/p/2166177.html
Copyright © 2011-2022 走看看