NS2下的TCP和TCPReno模块分析剖析Word下载.docx
《NS2下的TCP和TCPReno模块分析剖析Word下载.docx》由会员分享,可在线阅读,更多相关《NS2下的TCP和TCPReno模块分析剖析Word下载.docx(30页珍藏版)》请在冰豆网上搜索。
//本分组头在分组中的偏移量
inlinestaticint&
offset(){returnoffset_;
}//取得偏移量值
inlinestatichdr_tcp*access(Packet*p){//用于从分组获取本TCP分组头
return(hdr_tcp*)p->
access(offset_);
}
/*用于获取前面参数的子程序*/
double&
ts(){return(ts_);
ts_echo(){return(ts_echo_);
int&
seqno(){return(seqno_);
reason(){return(reason_);
};
其中ts_、ts_echo_用于计算rtt时间,offset_记录下本TCP分组头在该分组中的偏移,而acess(*p)则根据此偏移量来获取该分组。
TCP的发端和收端就是通过发送本端TCP分组头和接收对端分组头,来获取数据包的序号等信息,完成双方的通信。
TCP模块参数介绍
除了分组头的参数,TCP中的大量参数在TCP代理的类定义classTcpAgent中给出。
下面列出了常用的参数:
TracedIntt_rtt_;
/*往返时间RTT*/
TracedIntt_srtt_;
/*平滑的RTT时间*/
TracedIntt_rttvar_;
/*RTT的抖动平均偏差(均方值)*/
TracedIntt_backoff_;
/*计算RTO的回退因子,每次超时,有RTO=RTO*2。
*/
TracedIntt_rtxcur_/*重传超时的时长。
intT_SRTT_BITS;
/*用于更新SRTT的指数权值*/
intT_RTTVAR_BITS;
/*用于更新RTTVAR的指数权值*/
doublets_peer_;
/*对方发送的最后一个ACK的时间戳*/
/*对方发送的最后一个ACK的回应时间戳,表示本方相应序号发送的时间*/
intts_option_;
/*=1表示应用程序采用时间戳记录到分组头的方式来计算RTT,也可以使用本端记录RTT样本时间的方式,相应参数为rtt_ts*/
inttimerfix_;
/*修改后的更新RTO定时器策略,设为1表示采用在更新RTT以后更新RTO,而不是在更新RTT以前更新RTO定时器的策略*/
doubleboot_time_;
/*系统在两个TCP时钟之间启动经过的时间*/
doublewnd_;
//接收方的通知窗口,在单向TCP程序中为固定值
doublewnd_th_;
//窗口门限,用于计算awnd
doubletcp_tick_;
//TCP时间间隔,即一个“TCP嘀哒”的时长
intwnd_init_option_;
/*设为1表示使用默认初始启动窗口,设为2表示使用大的初始启动窗口*/
doubledecrease_num_;
/*倍数减少策略因子,默认为0.5*/
doubleincrease_num_;
/*线性增加策略因子,默认为1.0*/
intprecision_reduce_;
/*=1表示按双精度减少CWND*/
intmaxburst_;
/*一次最多允许发送的分组数量*/
TracedIntdupacks_;
/*重复ACK的个数*/
TracedIntcurseq_;
/*由应用程序给出字节数算出的总分组数*/
TracedInthighest_ack_;
//最大的ACK号,快速重传不会影响
intlast_ack_;
//最大“连续”ACK号,快速重传时保持不变
TracedDoublecwnd_;
/*拥塞窗口,用于计算发送窗口。
而发送窗口为min(cwnd,wnd),即拥塞窗口和对端通告窗口的较小者。
TracedIntssthresh_;
/*慢启动门限*/
TracedIntmaxseq_;
/*用于Karnalgorithm,记录已发送分组的最大序号*/
intlast_cwnd_action_;
/*用于记录上次拥塞动作,如超时、重传等*/
intrtt_active_;
/*=1表示RTT样本还没收到,即还未收到发送RTT样本的ACK*/
intrtt_seq_;
/*RTT样本号,该样本的发送时间被记录了下来,用于计算RTT*/
doublertt_ts_;
/*RTT样本rtt_seq发送的时间戳*/
intfirst_decrease_;
/*是否拥塞窗口第一次减少,用于防止第一次减少时慢启动门限就减少一半*/
RtxTimerrtx_timer_;
/*重传定时器*/
doublemaxrto_;
/*RTO的最大允许值*/
doubleminrto_;
/*RTO的最小允许值*/
intsyn_;
/*=1用于模拟TCP起始连接的握手*/
intdelay_growth_;
/*在收到第一个ACK后才开启cwnd,用于模拟TCP握手*/
intclosed_;
/*用于标志连接是否要中断*/
TCP程序分析
要编写TCPVeno模块,必须对TCP基本模块进行了解,下面对TCP(这里指TCPTAHOE)用到的各子程序进行大致的介绍。
需要注意的是,由于是单向TCP,程序中的发送和接收没有使用对方的窗口大小通告,二是将对端的通告窗口设为一个常量wnd_,使本端发送窗口为win=min(cwnd_,wnd_),发送窗口范围为:
highest_ack----highest_ack+win。
这在子程序output()和recv()中可以看到。
先对各子程序的功能作一个说明(下面的子程序均略去了参数):
TcpAgent():
是构造函数,用于初始化数据、参数绑定。
delay_bind_init_all()、delay_bind_dispatch():
用于延迟绑定,即在自己编写的tcl程序中绑定参数。
traceAll()、traceVar()、trace()、trace_event():
用于在*.tr文件中跟踪希望跟踪希望的变量。
set_initial_window():
用于握手连接时初始化拥塞窗口。
由reset()调用。
reset():
仅被delay_bind_init_all()调用,用于在连接前初始化各参数。
rtt_init():
也是被reset()调用,用于初始化t_rtt_、t_srtt_、t_rttvar_、t_rtxcur_和回退(补偿)因子t_backoff_。
rtt_timeout():
被set_rtx_timer()所调用,用于设定超时值。
rtt_update():
由newack()调用,而newack()被recv_newack_helper()调用,而recv_newack_helper()最终由recv()调用。
用于计算RTT值,并由rtt计算重传超时的时长t_rtxcur_。
算得的t_rtxcur_被rtt_timeout()调用,rtt_timeout()再被set_rtx_timer()调用。
rtt_backoff():
在reset_rtx_timer()中用到,超时后将回退因子t_backoff翻倍,即用于将RTO进行翻倍。
Output():
用于发送单个分组,被send_much()(发送多个分组)、send_one()(发送单个)等调用。
并不直接用于发送单个分组。
sendmsg():
该程序由上层应用调用,用来产生TCP流,完成tcp连接、传输、结束全过程,而不用管数据具体怎么传输。
window():
用于计算整型的发送窗口,发送窗口=min(wnd_,cwnd_)。
windowd():
用于计算双精度型的发送窗口。
send_much():
被recv()、timeout()、timeout_nonrtx()、sendmsg()等调用,用于在发送窗口范围内把尽可能多的数据发出去。
reset_rtx_timer():
被timeout()、dupack_action()调用,用于重置重传定时器。
set_rtx_timer():
被reset_rtx_timer()调用,用于设置重传定时器。
newtimer():
只被newack()调用,用来在收到新ACK时设置重传定时器,参数pkt是接收到的ACK分组。
opencwnd():
仅被recv_newack_helper()调用,recv_newack_helper()再被recv()调用,在收到新ACK后,用于慢启动和拥塞避免,只调整CWND,而不调整SSTHRESH。
slowdown():
被timeout()、dupack_action()等调用,用于在超时、重复ACK时降低拥塞窗口CWND和慢启动门限SSTHRESH。
newack():
被recv_newack_helper()调用,recv_newack_helper再被recv()调用,用于处理收到新ACK时记录的更新、RTT的更新等。
recv_newack_helper():
由recv()调用,处理收到新ACK的情况(调用了newack()),及处理cwnd设置,关闭连接等。
send_one():
TCPTAHOE使用了收到一个或两个重复ACK,就重传分组的策略,send_one()用于此时单个分组的发送。
dupack_action():
在recv()中,用于收到3个重复ACK后的快速重传,减少CWND和SSTHRESH,并重启重传定时器。
recv():
用于对收到的每一个ACK进行处理,用于新ACK的处理(recv_newack_helper()),重复ACK的处理(dupack_action()),以及处理后调用sendmuch(),继续发送尽可能多的数据。
timeout_nonrtx():
处理非重传的超时,即用于将发送数据进行随机延迟,模拟网络的延迟情况。
timeout():
直接被几个定时器的expire()调用,即在处理超时,根据超时类型改变慢启动门限和拥塞窗口。
RtxTimer:
:
expire():
重传时钟,调用了timeout(),即在超时后立即进行超时处理。
trace_event():
用于事件的跟踪,可以指定需要跟踪的参数。
此处只列出了各子程序的功能,具体分析见附录。
NS-2下的TCPReno模块分析
根据前面的分析,TCPVeno是在TCPReno上做了一些修改的。
在NS-2上,也沿用了TCPReno的一些算法。
因此,对Reno进行分析是很有必要的。
我们知道,TCP对于TCPTahoe来说,增加了快速重传和快速恢复的处理。
相对TCPTahoe,重载了window()、windowd()、recv()、dupack_action()、timeout()几个函数,增加了allow_fast_retransmit()函数。
下面是具体的分析:
intRenoTcpAgent:
window()//用于计算整形的发送窗口
{
intwin=int(cwnd_)+dupwnd_;
/*使用dupwnd_是为了限制传输机制,在快速重传时,dupwnd_=numdupacks_,增加发送窗口,在dupack_>
numdupacks_时,即快速恢复时,dupwnd_每次还要+1*/
if(frto_==2){
win=force_wnd
(2);
if(win>
int(wnd_))//比较接收窗口和加上dupwnd_的拥塞窗口win=int(wnd_);
return(win);
//得到发送窗口
}
doubleRenoTcpAgent:
windowd()//同上,不同的是得到的是双精度的发送窗口
doublewin=cwnd_+dupwnd_;
wnd_)
win=wnd_;
voidRenoTcpAgent:
recv(Packet*pkt,Handler*)
hdr_tcp*tcph=hdr_tcp:
access(pkt);
//
intvalid_ack=0;
……
if(tcph->
seqno()>
last_ack_){//本ACK序号>
上次记录的ACK序号,表示是新的ACK
……
dupwnd_=0;
//新的ACK,设置dupwnd_=0,用于下次计算发送窗口win
recv_newack_helper(pkt);
if(last_ack_==0&
&
delay_growth_){//是连接以来的第一个ACK
cwnd_=initial_window();
//初始化CWND
}
}elseif(tcph->
seqno()==last_ack_){//本次不是新ACK,是上次的重复ACK
return;
//以下是重复数ACK=1-2、3和3以上的三种情况的处理
if(++dupacks_==numdupacks_){
//默认numdupacks_=3,有3个重传就执行快速恢复和快速重传
dupack_action();
//改变SSTHRESH、CWND,并重设定时器超时
if(!
exitFastRetrans_)//exitFastRetrans_默认为1,reno专用
dupwnd_=numdupacks_;
}elseif(dupacks_>
numdupacks_&
(!
exitFastRetrans_
//超过numdupacks_次重传ACK,且上次也是重传ACK
||last_cwnd_action_==CWND_ACTION_DUPACK)){
++dupwnd_;
//执行快速恢复:
在超过numdupacks_次重传ACK后,
//每次再收到一个重复ACK,使发送窗口+1,加快发送,fastrecovery
}elseif(dupacks_<
singledup_){
//如果是1次到2次重复ACK,发送一个新分组,这是限制传输机制
send_one();
if(valid_ack||aggressive_maxburst_)//aggressive_maxburst_默认值为1
if(dupacks_==0||dupacks_>
numdupacks_-1)
//如果是新的ACK或者重复ACK数>
=3,尽量发送更多的数据
send_much(0,0,maxburst_);
//即如果重复ACK=1或者2,则不这样传,而按照上面只发送一个分组
dupack_action()//用于处理快速重传
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
//执行快速重传,SSTHRESH和CWND都降低一半
reset_rtx_timer(1,0);
//*重设重传定时器,mild=1,使t_seqno_!
=highest_ack_+1,保持原来的值
output(last_ack_+1,TCP_REASON_DUPACK);
//重传,即发送上次未确认的分组
dupwnd_=numdupacks_;
//发送窗口增加数
return;
timeout(inttno)
if(tno==TCP_TIMER_RTX){//本次超时是否用于重传
//超时后,dupwnd_和dupacks_置零,重新进行统计
dupacks_=0;
if(bug_fix_)recover_=maxseq_;
TcpAgent:
timeout(tno);
//执行TCPVegas的超时
}else{
timeout_nonrtx(tno);
//非重传超时的情况
小结
本章首先对NS仿真环境进行了介绍,随后着重分析了NS环境下的TCP协议,列出了TCP分组头,TCP参数和TCP的各个子程序的作用。
在附录中,对TCP协议进行了详细地分析。
随后对TCPReno协议进行了分析,了解了其工作原理,为建立TCPVeno模块做好了准备。
附录:
NS下的TCP协议分析
为方便协议的分析和节约篇幅起见,略去一些重复的和不重要的细节,以“…”代替。
另外此处分析的协议是ns2.29版本的tcp.cc文件,而不是实验中的ns2.26。
相对于后者,2.29作了一些改进。
以下是具体的协议分析:
TCP类的定义:
staticclassTcpClass:
publicTclClass{
public:
TcpClass():
TclClass("
Agent/TCP"
){}
//用于在TCL中使用该协议,即TCL和C++的绑定
TclObject*create(int,constchar*const*){
return(newTcpAgent());
}class_tcp;
//TCP类
//用于初始化和绑定TCP的参数,绑定的参数在ns-default.tcl中设定默认参数。
TcpAgent:
TcpAgent():
Agent(PT_TCP),…,frto_(0)//构造函数,此处初始化了参数
#ifdefTCP_DELAY_BIND_ALL
#else/*!
TCP_DELAY_BIND_ALL*/
bind("
t_seqno_"
&
t_seqno_);
//绑定各参数到TCL
ncwndcuts1_"
ncwndcuts1_);
#endif
//延迟绑定,用于在TCL脚本中声明该参数,使其参数在TCL应用程序中可改变。
改变时使用类似的语句。
同样,其默认参数也在ns-default.tcl中。
voidTcpAgent:
delay_bind_init_all()
delay_bind_init_one("
window_"
);
spurious_response_"
#endif/*TCP_DELAY_BIND_ALL*/
Agent:
delay_bind_init_all();
reset();
//用于初始化各个TCP参数
//将本C++中的参数同ns-default.tcl中的参数绑定,C++中的window_,TCL中为wnd_
intTcpAgent:
delay_bind_dispatch(constchar*varName,constchar*localName,TclObject*tracer)
if(delay_bind(varName,localName,"
wnd_,tracer))returnTCL_OK;
if(delay_bind_bool(varName,localName,"
s