ARQ Module Program Update
Note: The content in this series is extracted from the book, Introduction to Network Simulator NS2. You may have to read chapter 14 of the book for better understanding.
ARQ (Automatic Repeat reQuest) is a protocol which combats with transmission error using packet retransmission. The ARQ program provided in book is based on NS2 version 2.30. Unfortunately, NS2 version 2.35 makes a significant change, and breaks the provided program. In this post, I will give you the ARQ program which works on NS-2.35
Here are the files for the program
#include "connector.h"
class ARQTx;
enum ARQStatus {IDLE,SENT,ACKED,RTX,DROP};
class ARQHandler : public Handler {
public:
ARQHandler(ARQTx& arq) : arq_tx_(arq) {};
void handle(Event*);
private:
ARQTx& arq_tx_;
};
class ARQTx : public Connector {
public:
ARQTx();
void recv(Packet*, Handler*);
void nack(Packet*);
void ack();
void resume();
protected:
ARQHandler arqh_;
Handler* handler_;
Packet* pkt_;
ARQStatus status_;
int blocked_;
int retry_limit_;
int num_rtxs_;
};
class ARQRx : public Connector {
public:
ARQRx() {arq_tx_=0; };
int command(int argc, const char*const* argv);
virtual void recv(Packet*, Handler*);
protected:
ARQTx* arq_tx_;
Packet *pkt_; // for delay
Handler *handler_; // for delay
double delay_; // for delay
Event event_; // for delay
};
class ARQAcker : public ARQRx {
public:
ARQAcker() {};
//virtual void recv(Packet*, Handler*); // for NO delay
virtual void handle(Event*); // for delay
};
class ARQNacker : public ARQRx {
public:
ARQNacker() {};
//virtual void recv(Packet*, Handler*); // for NO delay
virtual void handle(Event*); // for delay
};
#include "arq.h"
static class ARQTxClass: public TclClass {
public:
ARQTxClass() : TclClass("ARQTx") {}
TclObject* create(int, const char*const*) {
return (new ARQTx);
}
} class_arq_tx;
static class ARQAckerClass: public TclClass {
public:
ARQAckerClass() : TclClass("ARQAcker") {}
TclObject* create(int, const char*const*) {
return (new ARQAcker);
}
} class_arq_acker;
static class ARQNackerClass: public TclClass {
public:
ARQNackerClass() : TclClass("ARQNacker") {}
TclObject* create(int, const char*const*) {
return (new ARQNacker);
}
} class_arq_nacker;
void ARQHandler::handle(Event* e)
{
arq_tx_.resume();
}
ARQTx::ARQTx() : arqh_(*this)
{
num_rtxs_ = 0; retry_limit_ = 0; handler_ = 0;
pkt_ = 0; status_ = IDLE; blocked_ = 0;
bind("retry_limit_", &retry_limit_);
}
void ARQTx::recv(Packet* p, Handler* h)
{
if (status_!=IDLE) {
fprintf(stderr,"Error at ARQTx::recv, a packet is received when the status is not IDLE\n");
abort();
}
if (h != 0)
handler_ = h;
status_ = SENT;
if (&arqh_==0) {
fprintf(stderr, "Error at ARQTx::recv, Cannot transmit when &arqh_ is Null\n");
abort();
}
blocked_ = 1;
send(p,&arqh_);
}
void ARQTx::ack()
{
if (status_!=SENT) {
fprintf(stderr,"Error at ARQTx::ack, an ACK is received when the status is not SENT\n");
abort();
}
num_rtxs_ = 0; status_ = ACKED;
if (!blocked_) {
if (handler_ == 0) {
fprintf(stderr,"Error at ARQTx::ack, handler_ is null\n");
abort();
}
status_ = IDLE; handler_->handle(0);
}
}
void ARQTx::nack(Packet* p)
{
if (status_!=SENT) {
fprintf(stderr,"Error at ARQTx::nack, a NACK is received when the status is not SENT\n");
abort();
}
num_rtxs_++;
if( num_rtxs_ <= retry_limit_) {
if (!blocked_) {
status_ = SENT;
if (&arqh_==0) {
fprintf(stderr, "Error at ARQTx::nack, Cannot transmit when &arqh_ is Null\n");
abort();
}
pkt_ = 0; blocked_ = 1;
send(p,&arqh_);
} else {
pkt_ = p;
status_ = RTX;
}
} else {
if (!blocked_) {
status_ = IDLE;
if (handler_ == 0) {
fprintf(stderr,"Error at ARQTx::nack, handler_ is null\n");
abort();
}
pkt_ = 0; drop(p);
handler_->handle(0);
} else {
pkt_ = p;
status_ = DROP;
}
}
}
void ARQTx::resume()
{
blocked_ = 0;
if ( status_ == ACKED ) {
if (handler_ == 0) {
fprintf(stderr,"Error at ARQTx::resume, handler_ is null\n");
abort();
}
status_ = IDLE; handler_->handle(0);
} else if ( status_ == RTX ) {
if (&arqh_==0) {
fprintf(stderr, "Error at ARQTx::resume, Cannot transmit when &arqh_ is Null\n");
abort();
}
status_ = SENT; blocked_ = 1;
send(pkt_,&arqh_);
} else if ( status_ == DROP ) {
if (handler_ == 0) {
fprintf(stderr,"Error at ARQTx::resume, handler_ is null\n");
abort();
}
status_ = IDLE; drop(pkt_);
handler_->handle(0);
}
}
int ARQRx::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "attach-ARQTx") == 0) {
if (*argv[2] == '0') {
tcl.resultf("Cannot attach NULL ARQTx\n");
return(TCL_ERROR);
}
arq_tx_ = (ARQTx*)TclObject::lookup(argv[2]);
return(TCL_OK);
}
} return Connector::command(argc, argv);
}
//====================================================
//=============== class ARQAcker ==================
//=================================================
// void ARQAcker::recv(Packet* p, Handler* h)
// {
// arq_tx_->ack();
// send(p,h);
// }
//====================================================
//=============== class ARQNacker ==================
//=================================================
// void ARQNacker::recv(Packet* p, Handler* h)
// {
// arq_tx_->nack(p);
// }
// ============== ADD FOR DELAY ======================
void ARQRx::recv(Packet* p, Handler* h)
{
pkt_ = p; handler_ = h;
if (delay_ > 0)
Scheduler::instance().schedule(this, &event_, delay_);
else
handle(&event_);
}
//====================================================
//=============== class ARQAcker ==================
//=================================================
void ARQAcker::handle(Event* e)
{
arq_tx_->ack();
send(pkt_,handler_);
}
//====================================================
//=============== class ARQNacker ==================
//=================================================
void ARQNacker::handle(Event* e)
{
arq_tx_->nack(pkt_);
}
# usage: ns <scriptfile> <err_rate> <num_rtx>
SimpleLink instproc link-arq { limit } {
$self instvar link_ link_errmodule_ queue_ drophead_ head_
$self instvar tARQ_ acker_ nacker_
set tARQ_ [new ARQTx]
set acker_ [new ARQAcker]
set nacker_ [new ARQNacker]
$tARQ_ set retry_limit_ $limit
$acker_ attach-ARQTx $tARQ_
$nacker_ attach-ARQTx $tARQ_
$tARQ_ target [$queue_ target]
$tARQ_ drop-target $drophead_
$queue_ target $tARQ_
$acker_ target [$link_errmodule_ target]
$link_errmodule_ target $acker_
$link_errmodule_ drop-target $nacker_
}
Simulator instproc link-arq {limit from to} {
set link [$self link $from $to]
$link link-arq $limit
}
proc show_tcp_seqno {} {
global tcp ns x
puts "At [$ns now], The tcp sequence number is [$tcp set t_seqno_]"
}
#=== Create the Simulator, Nodes, and Links ===
set ns [new Simulator]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]
$ns duplex-link $n1 $n2 5M 2ms DropTail
$ns duplex-link $n2 $n3 5M 2ms DropTail
$ns duplex-link $n1 $n3 5M 2ms DropTail
#=== Create error and ARQ module ===
set em [new ErrorModel]
$em set rate_ [lindex $argv 0]
$em set enable_ 1
$em unit pkt
$em set bandwidth_ 5M
$em ranvar [new RandomVariable/Uniform]
$em drop-target [new Agent/Null]
$ns link-lossmodel $em $n1 $n3
set num_rtx [lindex $argv 1]
$ns link-arq $num_rtx $n1 $n3
#=== Set up a TCP connection ===
set tcp [new Agent/TCP]
set sink [new Agent/TCPSink]
set ftp [new Application/FTP]
$ns attach-agent $n1 $tcp
$ns attach-agent $n3 $sink
$ftp attach-agent $tcp
$ns connect $tcp $sink
$ns at 0.0 "$ftp start"
$ns at 100.0 show_tcp_seqno
$ns at 100.1 "exit 0"
$ns run
======================================================
T. Issaraiyakul and E. Hossain, “Introduction to Network Simulator NS2”, Springer 2009. Buy it now from
You may also find lecture notes and other resource at the following website: http://www.ece.ubc.ca/~teerawat/NS2.htm