(转)添加一个新的应用和代理(二)

时间:2021-02-05 03:01:36

接下来继续分析mm-app.h和mm-app.cc

mm-app.h:

//Author;Vivian
//File:mm-app.h
//Written:08/25/10

#include "timer-handler.h"
#include "packet.h"
#include "app.h"
#include "udp-mm.h"

//定义接收者接收的报信息度量
struct pkt_accounting {
 int last_seq;     //最新收到的mm报
 int last_scale;    //最新收到的发送速率确认报
 int lost_pkts;     //至最新确认时丢失的包数量
 int recv_pkts;    //已经收到的报
 double rtt;      //周期
};

class MmApp;

//发送者用下面这个定时器调度下一个应用报传送时间
class SendTimer : public TimerHandler {
public:
 SendTimer(MmApp* t) : TimerHandler(),t_(t) {}
 inline virtual void expire(Event*);
protected:
 MmApp* t_;   //指向对应的应用类
};

//接收者用下面这个定时器决定下个ack报的传送时间
class AckTimer : public TimerHandler {
public:
 AckTimer(MmApp* t) : TimerHandler(),t_(t) {}
 inline virtual void expire(Event*);
protected:
 MmApp* t_;
};

//多媒体应用类定义
class MmApp : public Application {
public:
 MmApp();
 void send_mm_pkt();    //called by SendTimer:expire(Sender)
 void send_ack_pkt();   //called by AckTimer:expire(Receiver)
protected:
 int command(int argc,const char*const* argv);
 void start();     //开始发送数据包
 void stop();     //停止发送数据包(发送端)
private:
 void init();
 inline double next_snd_time();              //Sender
 virtual void recv_msg(int nbytes,const char *msg = 0);  //Sender/Receiver
 void set_scale(const hdr_mm *mh_buf);           //Sender
 void adjust_scale(void);                 //Receiver
 void account_recv_pkt(const hdr_mm *mh_buf);       //Receiver
 void init_recv_pkt_accounting();             //Receiver
 
 double rate[5];                  //5种传输速率
 double interval_;                 //应用数据包传送间隔
 int pktsize_;                   //应用数据包大小
 int random_;                   //if 1 add randomness to the interval??
 int running_;                   //if 1 application is running??
 int seq_;                     //应用数据包序号
 int scale_;                    //多媒体度量参数
 pkt_accounting p_accnt;
 SendTimer snd_timer_;
 AckTimer ack_timer_;
};       


 该头文件主要定义了一个发送定时器、发送ACK包定时器以及MmApp类。定时器到期后即调用MmApp类的send_mm_pkt()和send_ack_pkt()。next_snd_time()用来确定下次发包的时间。set_scale是发送端用来确定下一次用什么速率发送MM包。adjust_scale()是接收端用来调整度量值的。account_recv_accounting是接收端用来计算度量值的。snd_timer_和ack_timer_作为定时器的对象现在成为了MmApp的私有成员。

mm-app.cc:部分代码信息

1、//当snd_timer_ 到期,调用MmApp:send_mm_pkt(),t在头文件中定义了是MmApp类的一个对象

void SendTimer::expire(Event*) 
{
 t_->send_mm_pkt();    //定时器到时后发送packets
}

//当ack_timer_到期,调用MmApp::send_ack_pkt()
void AckTimer::expire(Event*)
{
 t_->send_ack_pkt();
}

2、

//tcl解释器
int MmApp::command(int argc,const char*const* argv)
{
 Tcl& tcl = Tcl::instance();
 
 if(argc == 3) {
  if(strcmp(argv[1],"attach-agent") == 0) {
   agent_ = (Agent*) TclObject::lookup(argv[2]);
   if (agent_ == 0) {
    tcl.resultf("no such agent %s",argv[2]);
    return (TCL_ERROR);
   }
   //确认底层代理支持多媒体应用
   if(agent_->supportMM()) {   //如果supportMM()返回1,说明是MM包
    agent_->enableMM();
   }
   else {
    tcl.resultf("agent \"%s\" does not support MM Application",argv[2]);
    return(TCL_ERROR);
   }
   agent_->attachApp(this);
   return(TCL_OK);
  }
 }
 return (Application::command(argc,argv));
}

3、

void MmApp::init()
{
 scale_ = 0;     //以最小速率开始传送
 seq_ = 0;      //MM报序号从0开始
 interval_ = (double)(pktsize_ << 3)/(double)rate[scale_];    //??pktsize_ << 3
}

void MmApp::start()
{
 init();
 running_ = 1;
 send_mm_pkt();
}

void MmApp::stop()
{
 running_ = 0;
}

这部分主要是一些初始化的工作

4、

//发送应用数据包
void MmApp::send_mm_pkt()
{
 hdr_mm mh_buf;     //定义具有hdr_mm结构体变量mh_buf
 if(running_) {
 //下面信息会在包创建后写入MM的报头传递给UDPmm 代理
 mh_buf.ack = 0;      //this is a MM packet,ack等于0说明是MM包
 mh_buf.seq = seq_++;
 mh_buf.nbytes = pktsize_;  //MM报的大小(而非UDP报大小)
 mh_buf.time = Scheduler::instance().clock();
 mh_buf.scale = scale_;    //当前scale值,初始化时为0
 agent_->sendmsg(pktsize_,(char*) &mh_buf);   //传递给UDPmm**
 
 //重新调用send_pkt 定时器,
 double next_time_ = next_snd_time();   //计算下一次发包的时间
 if(next_time_ > 0) snd_timer_.resched(next_time_);
 }
}

应用代理发包时需要把MM报头添加到Packet中,最后调用agent->sendmsg()函数把包传递到下层的UDPmm代理。接着重新定时。

5、

//调度下次传递数据包的传送时间
double MmApp::next_snd_time()
{
 interval_ = (double)(pktsize_ << 3)/(double)rate[scale_];
 double next_time_ = interval_;
 if(random_)                  //random_为1表示出发这个随机时间,否则直接把interval_赋值给next_time_
  next_time_ += interval_*Random::uniform(-0.5,0.5);
 return next_time_;
}

6、

//发送者通过接收者的notiifes设定scale
void MmApp::set_scale(const hdr_mm *mh_buf)
{
 scale_ = mh_buf->scale;
}

7、

void MmApp::account_recv_pkt(const hdr_mm *mh_buf)
{
 double local_time = Scheduler::instance().clock();
 //计算RTT
 if(mh_buf->seq == 0) {
  init_recv_pkt_accounting();
  p_accnt.rtt = 2*(local_time - mh_buf->time);  //rtt是当前时间-发送者发来包中报头的时间
 }
 else
  p_accnt.rtt = 0.9*p_accnt.rtt +0.1*2*(local_time - mh_buf->time);
  
  //数收到的报数量并计算包的丢失
  p_accnt.recv_pkts ++;
  p_accnt.lost_pkts += (mh_buf->seq - p_accnt.last_seq - 1);
  p_accnt.last_seq = mh_buf->seq;


}

接收端计算接收到的MM包的相关信息,如果接收到的seq为0,则初始化accounting()相关信息,否则,计算RTT如果收到的是第一个MM包,rtt为接收时延的2倍,否则rtt按 p_accnt.rtt = 0.9*p_accnt.rtt +0.1*2*(local_time - mh_buf->time)公式计算。此外,还需计算接收包的数量、封包丢失的数量(利用seq)以及更新最新收到封包的序列号。

8、

void MmApp::send_ack_pkt(void)
{
 double local_time = Scheduler::instance().clock();
 adjust_scale();
 //发送ack信息
 hdr_mm ack_buf;
 ack_buf.ack = 1;       //this is a ack packet
 ack_buf.time = local_time;
 ack_buf.nbytes = 40;  //ack报大小为40
 ack_buf.scale = p_accnt.last_scale;
 agent_->sendmsg(ack_buf.nbytes,(char*) &ack_buf);  //传给UDP
 
 //调度下一次ack时间
 ack_timer.resched(p_accnt.rtt);
 
}

9、

void MmApp::adjust_scale(void)
{
 if(p_accnt.recv_pkts > 0) {
  if(p_accnt.lost_pkts > 0)
   p_accnt.last_scale = (int) (p_accnt.last_scale / 2);    //如果丢包,scale减半
  else {
   p_accnt.last_scale ++;   //网络畅通,scale加1
   if(p_accnt.last_scale > 4) p_accnt.last_scale = 4;
  }
 }
 p_accnt.recv_pkts = 0;
 p_accnt.lost_pkts = 0;
}

 

计算好accounting相关信息后,接收端调整scale的度量值。如果发现有丢包的情况,那么接收端就把上次设定的scale值减半,如果网络畅通,则scale加1。增加发送端的发送速率。当然最大不能超过4。同时清零相关信息。最后发送ack包。这里指的注意的是,ack包是定期发送的。