接下来继续分析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包是定期发送的。