/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990, 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-reno.cc,v 1.42 2005/07/13 03:51:32 tomh Exp $ (LBL)";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include "ip.h"
#include "tcp.h"
#include "flags.h"
static class RenoTcpClass : public TclClass {
public:
RenoTcpClass() : TclClass("Agent/TCP/Reno") {}
TclObject* create(int, const char*const*) {
return (new RenoTcpAgent());
}
} class_reno;
int RenoTcpAgent::window()
{
//
// reno: inflate the window by dupwnd_
// dupwnd_ will be non-zero during fast recovery,
// at which time it contains the number of dup acks
//
int win = int(cwnd_) + dupwnd_; //在快速恢复时,增加发送窗口,dupwnd_=0(初始)或者=numdupacks_,在dupack_>numdupacks_时,dupwnd_每次还要+1
if (frto_ == 2) {
// First ack after RTO has arrived.
// Open window to allow two new segments out with F-RTO.
win = force_wnd(2);
}
if (win > int(wnd_))
win = int(wnd_);
return (win);
}
double RenoTcpAgent::windowd()
{
//
// reno: inflate the window by dupwnd_
// dupwnd_ will be non-zero during fast recovery,
// at which time it contains the number of dup acks
//
double win = cwnd_ + dupwnd_;
if (win > wnd_)
win = wnd_;
return (win);
}
RenoTcpAgent::RenoTcpAgent() : TcpAgent(), dupwnd_(0)
{
}
void RenoTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
int valid_ack = 0;
if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
endQuickStart();
if (qs_requested_ == 1)
processQuickStart(pkt);
#ifdef notdef
if (pkt->type_ != PT_ACK) {
fprintf(stderr,
"ns: confiuration error: tcp received non-ack\n");
exit(1);
}
#endif
/* W.N.: check if this is from a previous incarnation */
if (tcph->ts() < lastreset_) {
// Remove packet and do nothing
Packet::free(pkt);
return;
}
++nackpack_;
ts_peer_ = tcph->ts();
if (hdr_flags::access(pkt)->ecnecho() && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
recv_frto_helper(pkt);
if (tcph->seqno() > last_ack_) { //本ACK序号>上次记录的ACK序号,表示是新的ACK
if (last_cwnd_action_ == CWND_ACTION_DUPACK) //*本次新ACK,则退出快速重传
last_cwnd_action_ = CWND_ACTION_EXITED; //*
dupwnd_ = 0; //*设置dupwnd_=0,用于下次计算发送窗口win
recv_newack_helper(pkt);
if (last_ack_ == 0 && delay_growth_) { //是连接以来的第一个ACK
cwnd_ = initial_window(); //初始化CWND
}
} else if (tcph->seqno() == last_ack_) { // 本次不是新ACK,是上次的重复ACK
if (hdr_flags::access(pkt)->eln_ && eln_) {
tcp_eln(pkt);
return;
}
//以下是重复数ACK=1-2、3和3以上的三种情况的处理
if (++dupacks_ == numdupacks_) { //*默认numdupacks_=3,有3个重传就执行快速恢复和快速重传
dupack_action(); //改变SSTHRESH、CWND,并重设定时器超时
if (!exitFastRetrans_) //*exitFastRetrans_默认为1,reno专用
dupwnd_ = numdupacks_;
} else if (dupacks_ > numdupacks_ && (!exitFastRetrans_ //*超过numdupacks_次重传ACK,且上次也是重传ACK
|| last_cwnd_action_ == CWND_ACTION_DUPACK )) {
++dupwnd_; //* 执行快速恢复:在超过numdupacks_次重传ACK后,每次再收到一个重复ACK,使发送窗口+1,加快发送,fast recovery
} else if (dupacks_ < numdupacks_ && singledup_ ) { //如果是1次到2次重复ACK,发送一个新分组 ,这是限制传输机制
send_one();
}
}
if (tcph->seqno() >= last_ack_) //本ACK序号>=上次ACK,表示是新ACK或重复ACK,是合法的
// Check if ACK is valid. Suggestion by Mark Allman.
valid_ack = 1;
Packet::free(pkt); //合法,回收该分组内存
#ifdef notyet
if (trace_)
plot();
#endif
/*
* Try to send more data
*/
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,则不这样传,而按照上面只发送一个分组
}
/*注: 限制传输机制(Limited Transmit mechanism)
我们知道,当前的TCP应用主要有两种重传机制-快速重传和超时重传。当TCP源端收到3个ACK副本时,
就会触发快速重传机制,此时源端重传丢失的数据包并且将拥塞窗口大小减半。这种情况下,TCP流往往能
够很快从丢包中恢复过来,重新回到原先的发送速率。但如果TCP源端没有收到3个ACK副本,例如拥塞窗口
大小小于4,那么TCP源端则需要等待相当长时间,以便超时重发。这样,小窗口的TCP流就很容易陷入不必
要的超时重发,使其吞吐量大大下降。
为了避免这种不必要的超时重传,一种改进办法就是只要TCP源端收到一个或者两个ACK副本,并且如果
通告窗口允许,便继续发送新的数据包。这是因为只要收到ACK副本,就表明有数据包已经离开网络被接受
端接收了,而此时源端还无法判断数据包是否被丢弃,根据“数据包守恒”原则,只要遵守拥塞窗口的规范,
也即同时在网络中传送的数据包数量不能超过拥塞窗口的大小(以数据包为单位),源端就可以继续发送新
的数据包。这种机制称为限制传输机制(Limited Transmit mechanism),这种机制对排序的数据包尤其有效。
限制传输机制可以使小窗口的TCP流很快从丢包中恢复过来。例如,对于拥塞窗口大小为4地TCP流,如果
其第二个数据包丢失,那么按传统地做法需要等待超时重传。而在限制传输机制下,当源端收到对第三个数据
包确认的ACK副本时(ACK中要求源端发送第二个数据包),继续发送新的数据包,最终源端可以收到三个ACK
副本从而触发快速重传,从而减少了不必要的超时重传。
*/
//last_cwnd_action_即上次对CWND的动作为DUPACK,则本次允许快速重传 //本次
int
RenoTcpAgent::allow_fast_retransmit(int last_cwnd_action_)
{
return (last_cwnd_action_ == CWND_ACTION_DUPACK);
}
/*
* Dupack-action: what to do on a DUP ACK. After the initial check
* of 'recover' below, this function implements the following truth
* table:
*
* bugfix ecn last-cwnd == ecn action
*
* 0 0 0 reno_action
* 0 0 1 reno_action [impossible]
* 0 1 0 reno_action
* 0 1 1 retransmit, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 retransmit, return
*/
//重复ACK满3个,执行下面程序
void
RenoTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_); //*上次是DUPACK状态,本次仍是重复ACK,允许本次快速重传
if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) { //*若允许快速重传,跳转
goto reno_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
/*
* What if there is a DUPACK action followed closely by ECN
* followed closely by a DUPACK action?
* The optimal thing to do would be to remember all
* congestion actions from the most recent window
* of data. Otherwise "bugfi
评论1