📄 tcp-fast.cc
字号:
* and try to resume the sequence.
*/
dupack_action();
} else if (dupacks_ < numdupacks(cwnd_) && singledup_ ) {
send_one();
}
}
}
if (valid_ack || aggressive_maxburst_)
if (dupacks_ == 0)
send_much(FALSE, 0, maxburst_);
} else {
/* we are in fast recovery */
cwnd_update_time = currentTime;
--pipe_;
if ((int)tcph->seqno() >= recover_) {
/* ACK indicates fast recovery is over */
recover_ = 0;
fastrecov_ = FALSE;
newack(pkt);
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
timeout_ = FALSE;
scb_->ClearScoreBoard();
/* New window: W/2 - K or W/2? */
} else if ((int)tcph->seqno() > highest_ack_) {
/* Not out of fast recovery yet.
* Update highest_ack_, but not last_ack_. */
highest_ack_ = (int)tcph->seqno();
scb_->UpdateScoreBoard (highest_ack_, tcph);
if (partial_ack_) {
/* partial_ack_ is needed to guarantee that */
/* a new packet is sent in response to a */
/* partial ack. */
partial_ack_action();
++pipe_;
if (firstpartial_ == 0) {
newtimer(pkt);
t_backoff_ = 1;
firstpartial_ = 1;
}
} else {
--pipe_;
newtimer(pkt);
t_backoff_ = 1;
/* If this partial ACK is from a retransmitted pkt,*/
/* then we decrement pipe_ again, so that we never */
/* do worse than slow-start. If this partial ACK */
/* was instead from the original packet, reordered,*/
/* then this might be too aggressive. */
}
} else if (timeout_ == FALSE) {
/* got another dup ack */
scb_->UpdateScoreBoard (highest_ack_, tcph);
if(scb_->CheckUpdate()) {
if (dupacks_ > 0)
dupacks_++;
}
}
if (valid_ack || aggressive_maxburst_)
send_much(FALSE, 0, maxburst_);
}
Packet::free(pkt);
}
/******************************************************************************/
void
FastTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_)) {
goto sack_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 "bugfix" might not prevent
* all unnecessary Fast Retransmits.
*/
reset_rtx_timer(1,0);
/*
* There are three possibilities:
* (1) pipe_ = int(cwnd_) - numdupacks_;
* (2) pipe_ = window() - numdupacks_;
* (3) pipe_ = maxseq_ - highest_ack_ - numdupacks_;
* equation (2) takes into account the receiver's
* advertised window, and equation (3) takes into
* account a data-limited sender.
*/
if (singledup_ && LimTransmitFix_) {
pipe_ = maxseq_ - highest_ack_ - 1;
}
else {
// numdupacks(cwnd_) packets have left the pipe
pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
}
fastrecov_ = TRUE;
scb_->MarkRetran(highest_ack_+1);
output(last_ack_ + 1, TCP_REASON_DUPACK);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
sack_action:
// we are now going into fast_recovery and will trace that event
trace_event("FAST_RECOVERY");
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
if (oldCode_) {
pipe_ = int(cwnd_) - numdupacks(cwnd_);
} else if (singledup_ && LimTransmitFix_) {
pipe_ = maxseq_ - highest_ack_ - 1;
}
else {
// numdupacks(cwnd_) packets have left the pipe
pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
}
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
reset_rtx_timer(1,0);
fastrecov_ = TRUE;
scb_->MarkRetran(highest_ack_+1);
output(last_ack_ + 1, TCP_REASON_DUPACK); // from top
/*
* If dynamically adjusting numdupacks_, record information
* at this point.
*/
return;
}
/*
* Process a packet that acks previously unacknowleges data, but
* does not take us out of Fast Retransmit.
*
* The need for a mechanism to ensure that Sack TCP sends a packet in
* response to a partial ACK has been discussed in
* "Challenges to Reliable Data Transport over Heterogeneous
* Wireless Networks", Hari Balakrishnan, 1998, and in
* "Responding to Spurious Timeouts in TCP", Andrei Gurtov and
* Reiner Ludwig, 2003.
*/
void
FastTcpAgent::partial_ack_action()
{
if (next_pkt_ < highest_ack_ + 1) {
next_pkt_ = highest_ack_ + 1;
}
// Output two packets in response to a partial ack,
// so as not to do worse than slow-start.
int i;
for (i = 1; i<=2; i++) {
int getNext = scb_->GetNextUnacked(next_pkt_);
if (getNext > next_pkt_) {
next_pkt_ = getNext;
}
if (t_seqno_ < next_pkt_) {
t_seqno_ = next_pkt_;
}
output(next_pkt_, TCP_REASON_PARTIALACK);
scb_->MarkRetran(next_pkt_);
++next_pkt_;
}
return;
}
void FastTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
/*
* IF DSACK and dynamic adjustment of numdupacks_,
* check whether a smaller value of numdupacks_
* would have prevented this retransmit timeout.
* If DSACK and detection of premature retransmit
* timeouts, then save some info here.
*/
dupacks_ = 0;
fastrecov_ = FALSE;
timeout_ = TRUE;
if (highest_ack_ > last_ack_)
last_ack_ = highest_ack_;
recover_ = maxseq_;
scb_->ClearScoreBoard();
if (highest_ack_ == maxseq_ && !slow_start_restart_) {
/*
* TCP option:
* If no outstanding data, then don't do anything.
*
* Note: in the USC implementation,
* slow_start_restart_ == 0.
* I don't know what the U. Arizona implementation
* defaults to.
*/
return;
};
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
reset_rtx_timer(0, 0);
++nrexmit_;
slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF);
cwnd_ = double(slowstart_);
newcwnd_ = 0;
send_much(0, TCP_REASON_TIMEOUT, maxburst_);
} else {
/* delayed-sent timer, with random overhead to avoid
* phase effect. */
send_much(1, TCP_REASON_TIMEOUT, 3);
}
}
void FastTcpAgent::send_much(int force, int reason, int maxburst)
{
register int found, npacket = 0;
int win = window();
int xmit_seqno;
found = 1;
if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
/* Save time when first packet was sent, for newreno --Allman */
if (t_seqno_ == 0)
firstsent_ = Scheduler::instance().clock();
/*
* as long as the pipe is open and there is app data to send...
*/
while (((!fastrecov_ && (t_seqno_ <= last_ack_ + win)) ||
(fastrecov_ && (pipe_ < int(cwnd_))))
&& t_seqno_ < curseq_ && found) {
if (overhead_ == 0 || force) {
found = 0;
int oldest_unacked_pkt = scb_->GetNextUnacked(last_ack_);
if (oldest_unacked_pkt != -1 &&
fasttime() - sendtime_[oldest_unacked_pkt%maxwnd_] > 2*avgRTT_){
xmit_seqno = oldest_unacked_pkt;
}
else
xmit_seqno = scb_->GetNextRetran ();
if (xmit_seqno == -1) {
if ((!fastrecov_ && t_seqno_<=highest_ack_+win)||
(fastrecov_ && t_seqno_<=highest_ack_+int(wnd_))) {
found = 1;
xmit_seqno = t_seqno_++;
}
} else if (recover_>0 && xmit_seqno<=highest_ack_+int(wnd_)) {
found = 1;
scb_->MarkRetran (xmit_seqno);
win = window();
}
if (found) {
output(xmit_seqno, reason);
if (t_seqno_ <= xmit_seqno)
t_seqno_ = xmit_seqno + 1;
npacket++;
pipe_++;
if (QOption_)
process_qoption_after_send () ;
if (qs_approved_ == 1) {
double delay = (double) t_rtt_ * tcp_tick_ / cwnd_;
delsnd_timer_.resched(delay);
return;
}
}
} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
/*
* Set a delayed send timeout.
* This is only for the simulator,to add some
* randomization if speficied.
*/
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
if (maxburst && npacket == maxburst)
break;
} /* while */
}
void
FastTcpAgent::output(int seqno, int reason)
{
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
double now = Scheduler::instance().clock();
hdr_flags* hf = hdr_flags::access(p);
hdr_ip *iph = hdr_ip::access(p);
int databytes = hdr_cmn::access(p)->size();
tcph->seqno() = seqno;
tcph->ts() = now;
tcph->reason() = reason;
/* if this is the 1st pkt, setup senttime[] and transmits[]
* I alloc mem here, instrad of in the constructor, to cover
* cases which windows get set by each different tcp flows */
if (seqno==0) {
maxwnd_ = int(wnd_);
if (sendtime_)
delete []sendtime_;
if (transmits_)
delete []transmits_;
if (cwnd_array_)
delete []cwnd_array_;
sendtime_ = new double[maxwnd_];
transmits_ = new int[maxwnd_];
cwnd_array_= new double[maxwnd_];
for(int i=0;i<maxwnd_;i++) {
sendtime_[i] = -1.;
transmits_[i] = 0;
cwnd_array_[i]=-1;
}
}
if (ecn_) {
hf->ect() = 1; // ECN capable transport.
}
/* Check if this is the initial SYN packet. */
if (seqno == 0) {
if (syn_) {
databytes= 0;
curseq_ += 1;
hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
}
if (ecn_) {
hf->ecnecho() = 1;
// hf->cong_action() = 1;
hf->ect() = 0;
}
}
else if (useHeaders_ == true) {
hdr_cmn::access(p)->size() += headersize();
}
// record a find grained send time and # of transmits
int index = seqno % maxwnd_;
sendtime_[index] = fasttime();
cwnd_array_[index]=avg_cwnd_last_RTT_;
++transmits_[index];
/* support ndatabytes_ in output - Lloyd Wood 14 March 2000 */
int bytes = hdr_cmn::access(p)->size();
ndatabytes_ += bytes;
ndatapack_++; // Added this - Debojyoti 12th Oct 2000
send(p, 0);
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
if (!rtt_active_) {
rtt_active_ = 1;
if (seqno > rtt_seq_) {
rtt_seq_ = seqno;
rtt_ts_ = now;
}
}
} else {
++nrexmitpack_;
nrexmitbytes_ += bytes;
}
if (!(rtx_timer_.status() == TIMER_PENDING))
/* No timer pending. Schedule one. */
set_rtx_timer();
}
/******************************************************************************/
/* Space out increments to cwnd as acknowledegements arrive */
/* cwndp points to the actual cwnd value */
/* cwnd_incre (0 or 1) specifies the desired amount to increase cwnd by */
/* (eventually) */
void
FastTcpAgent::fast_pace(TracedDouble *cwndp, int incre_4_cwnd)
{
if ( !avgRTT_ )
return;
double acks_per_period = (double)acks_last_rtt * fast_update_cwnd_interval_ / avgRTT_;
if ( incre_4_cwnd >= 1 )
{
cwnd_increments += incre_4_cwnd;
/* bc_spacing: target number of ACKs between increments */
bc_spacing = (short unsigned int)(acks_per_period/cwnd_increments);
/* bc_ack: number of ACKs since last increment */
bc_ack = 1;
}
else {
bc_ack ++;
}
if (cwnd_increments)
{
/* if cwnd small, increment immediately */
if (*cwndp <= 4) {
/* if increment would more than double cwnd, do it in stages */
if (*cwndp < cwnd_increments) {
cwnd_increments -= (unsigned int)*cwndp;
*cwndp += *cwndp;
}
else {
*cwndp += cwnd_increments;
cwnd_increments=0;
bc_spacing=0;
}
bc_ack=0;
}
else if (bc_ack >= bc_spacing)
{
(*cwndp)++;
cwnd_increments--;
bc_ack = 0;
}
}
}
/******************************************************************************/
/*
* return -1 if the oldest sent pkt has not been timeout (based on
* fine grained timer).
*/
int
FastTcpAgent::fast_expire(Packet* pkt)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
double elapse = fasttime() - sendtime_[(tcph->seqno()+1)%maxwnd_];
if (elapse >= 2 * avgRTT_) {
return(tcph->seqno()+1);
}
return(-1);
}
/******************************************************************************/
void
FastTcpAgent::fast_recv_newack_helper(Packet *pkt) {
newack(pkt);
if (ect_) {
if (!hdr_flags::access(pkt)->ecnecho())
ecn_backoff_ = 0;
if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = TRUE;
else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = FALSE;
}
if (!ect_ && hdr_flags::access(pkt)->ecnecho() &&
!hdr_flags::access(pkt)->cong_action())
ect_ = 1;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
if (QOption_ && curseq_ == highest_ack_ +1) {
cancel_rtx_timer();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -