您的位置:首页 > 其它

[翻译]NS下添加新协议

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

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

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

1.头文件

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

struct hdr_ping {
char ret;
double send_time;
};


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

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

class PingAgent : public Agent {
public:
PingAgent();
int command(int argc, const char*const* argv);
void recv(Packet*, Handler*);
protected:
int off_ping_;
};


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

附(整体代码):

/*
* File: Header File for a new 'Ping' Agent Class for the ns
*       network simulator
* Author: Marc Greis (greis@cs.uni-bonn.de), May 1998
*
*/

#ifndef ns_ping_h
#define ns_ping_h

#include "agent.h"
#include "tclcl.h"
#include "packet.h"
#include "address.h"
#include "ip.h"

struct hdr_ping {
char ret;
double send_time;
};

class PingAgent : public Agent {
public:
PingAgent();
int command(int argc, const char*const* argv);
void recv(Packet*, Handler*);
protected:
int off_ping_;
};

#endif


2.C++代码

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

static class PingHeaderClass : public PacketHeaderClass {
public:
PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping", sizeof(hdr_ping)) {}
} class_pinghdr;

static class PingClass : public TclClass {
public:
PingClass() : TclClass("Agent/Ping") {}
TclObject* create(int, const char*const*) {
return (new PingAgent());
}
} class_ping;


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

PingAgent::PingAgent() : Agent(PT_PING)
{
bind("packetSize_", &size_);
bind("off_ping_", &off_ping_);
}


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

int PingAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "send") == 0) {
// Create a new packet
Packet* pkt = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Set the 'ret' field to 0, so the receiving node knows
// that it has to generate an echo packet
hdr->ret = 0;
// Store the current time in the 'send_time' field
hdr->send_time = Scheduler::instance().clock();
// Send the packet
send(pkt, 0);
// return TCL_OK, so the calling function knows that the
// command has been processed
return (TCL_OK);
}
}
// If the command hasn't been processed by PingAgent()::command,
// call the command() function for the base class
return (Agent::command(argc, argv));
}


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

void PingAgent::recv(Packet* pkt, Handler*)
{
// Access the IP header for the received packet:
hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
// Access the Ping header for the received packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
if (hdr->ret == 0) {
// Send an 'echo'. First save the old packet's send_time
double stime = hdr->send_time;
// Discard the packet
Packet::free(pkt);
// Create a new packet
Packet* pktret = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
// Set the 'ret' field to 1, so the receiver won't send another echo
hdrret->ret = 1;
// Set the send_time field to the correct value
hdrret->send_time = stime;
// Send the packet
send(pktret, 0);
} else {
// A packet was received. Use tcl.eval to call the Tcl
// interpreter with the ping results.
// Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
// has to be defined which allows the user to react to the ping
// result.
char out[100];
// Prepare the output to the Tcl interpreter. Calculate the round
// trip time
sprintf(out, "%s recv %d %3.1f", name(),
hdrip->src_.addr_ >> Address::instance().NodeShift_[1],
(Scheduler::instance().clock()-hdr->send_time) * 1000);
Tcl& tcl = Tcl::instance();
tcl.eval(out);
// Discard the packet
Packet::free(pkt);
}
}


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

附(整体代码):

/*
* File: Code for a new 'Ping' Agent Class for the ns
*       network simulator
* Author: Marc Greis (greis@cs.uni-bonn.de), May 1998
*
*/

#include "ping.h"

static class PingHeaderClass : public PacketHeaderClass {
public:
PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping",
sizeof(hdr_ping)) {}
} class_pinghdr;

static class PingClass : public TclClass {
public:
PingClass() : TclClass("Agent/Ping") {}
TclObject* create(int, const char*const*) {
return (new PingAgent());
}
} class_ping;

PingAgent::PingAgent() : Agent(PT_PING)
{
bind("packetSize_", &size_);
bind("off_ping_", &off_ping_);
}

int PingAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "send") == 0) {
// Create a new packet
Packet* pkt = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Set the 'ret' field to 0, so the receiving node knows
// that it has to generate an echo packet
hdr->ret = 0;
// Store the current time in the 'send_time' field
hdr->send_time = Scheduler::instance().clock();
// Send the packet
send(pkt, 0);
// return TCL_OK, so the calling function knows that the
// command has been processed
return (TCL_OK);
}
}
// If the command hasn't been processed by PingAgent()::command,
// call the command() function for the base class
return (Agent::command(argc, argv));
}

void PingAgent::recv(Packet* pkt, Handler*)
{
// Access the IP header for the received packet:
hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
// Access the Ping header for the received packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
if (hdr->ret == 0) {
// Send an 'echo'. First save the old packet's send_time
double stime = hdr->send_time;
// Discard the packet
Packet::free(pkt);
// Create a new packet
Packet* pktret = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
// Set the 'ret' field to 1, so the receiver won't send another echo
hdrret->ret = 1;
// Set the send_time field to the correct value
hdrret->send_time = stime;
// Send the packet
send(pktret, 0);
} else {
// A packet was received. Use tcl.eval to call the Tcl
// interpreter with the ping results.
// Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
// has to be defined which allows the user to react to the ping
// result.
char out[100];
// Prepare the output to the Tcl interpreter. Calculate the round
// trip time
sprintf(out, "%s recv %d %3.1f", name(),
hdrip->src_ >> Address::instance().NodeShift_[1],
(Scheduler::instance().clock()-hdr->send_time) * 1000);
Tcl& tcl = Tcl::instance();
tcl.eval(out);
// Discard the packet
Packet::free(pkt);
}
}


3.必要的改变

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

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

enum packet_t {
PT_TCP,
PT_UDP,
......
// insert new packet types here
PT_TFRC,
PT_TFRC_ACK,
PT_PING,    //  packet protocol ID for our ping-agent
PT_NTYPE // This MUST be the LAST one
};


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

class p_info {
public:
p_info() {
name_[PT_TCP]= "tcp";
name_[PT_UDP]= "udp";
...........
name_[PT_TFRC]= "tcpFriend";
name_[PT_TFRC_ACK]= "tcpFriendCtl";

name_[PT_PING]="Ping";

name_[PT_NTYPE]= "undefined";
}
.....
}


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

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

Agent/Ping set packetSize_ 64


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

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


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

sessionhelper.o delaymodel.o srm-ssm.o \
srm-topo.o \
ping.o \
$(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o \
$(LIB_DIR)dmalloc_support.o \


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

4.Tcl代码

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

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


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

附(完全代码):

#Create a simulator object
set ns [new Simulator]

#Open a trace file
set nf [open out.nam w]
$ns namtrace-all $nf

#Define a 'finish' procedure
proc finish {} {
global ns nf
$ns flush-trace
close $nf
exec nam out.nam &
exit 0
}

#Create three nodes
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]

#Connect the nodes with two links
$ns duplex-link $n0 $n1 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail

#Define a 'recv' function for the class 'Agent/Ping'
Agent/Ping instproc recv {from rtt} {
$self instvar node_
puts "node [$node_ id] received ping answer from \
$from with round-trip-time $rtt ms."
}

#Create two ping agents and attach them to the nodes n0 and n2
set p0 [new Agent/Ping]
$ns attach-agent $n0 $p0

set p1 [new Agent/Ping]
$ns attach-agent $n2 $p1

#Connect the two agents
$ns connect $p0 $p1

#Schedule events
$ns at 0.2 "$p0 send"
$ns at 0.4 "$p1 send"
$ns at 0.6 "$p0 send"
$ns at 0.6 "$p1 send"
$ns at 1.0 "finish"

#Run the simulation
$ns run


  现在你可以试着做一些实验。Good Luck!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: