📄 tcp.cc
字号:
++cwnd_;
} else
fcnt_ = f;
break;
case 3:
f = awnd_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 4:
f = awnd_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 5:
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 6:
/* binomial controls */
cwnd_ += increase_num_ / (cwnd_*pow(cwnd_,k_parameter_));
break;
default:
#ifdef notdef
/*XXX*/
error("illegal window option %d", wnd_option_);
#endif
abort();
}
}
// if maxcwnd_ is set (nonzero), make it the cwnd limit
if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
cwnd_ = maxcwnd_;
// ***********
/*
if (window() < old_cwnd_ + 1)
return;
cur_thu = (double) old_cwnd_*size_*8.0 / wnd_thru[old_cwnd_];
if (cur_thu < 1.0 *last_wnd_thru)
{
// slow down, because throuput doesn't increase
cwnd_ = old_cwnd_-1;
old_cwnd_ --;
if (old_cwnd_ >1 && wnd_thru[old_cwnd_-1] >0)
last_wnd_thru = (old_cwnd_-1) * size_*8.0/wnd_thru[old_cwnd_-1];
else
last_wnd_thru = 0;
}
else
if(cur_thu < 1.05 * last_wnd_thru)
{ // hold here, because throuput
cwnd_ = old_cwnd_;
}
else
{
// increase cwnd
old_cwnd_ = window();
last_wnd_thru = cur_thu;
};
FILE *fp;
fp = fopen("trace","a+");
fprintf(fp,"in opencwnd\n%f:cwnd=%d\tcur_thu=%f\tlast_thu=%f\told_cwnd=%d\trtt=%f\n",Scheduler::instance().clock(),(int)cwnd_,cur_thu,last_wnd_thru,(int)old_cwnd_, t_myrtt);
for (i=1;i<16;i++)
fprintf(fp,"%f\t",i * size_ * 8.0/wnd_thru[i]);
fprintf(fp,"\n");
fclose(fp);
*/
return;
}
void
TcpAgent::slowdown(int how)
{
double win, halfwin, decreasewin;
int slowstart = 0;
// we are in slowstart for sure if cwnd < ssthresh
if (cwnd_ < ssthresh_)
slowstart = 1;
if (precision_reduce_) {
halfwin = windowd() / 2;
if (wnd_option_ == 6) {
/* binomial controls */
decreasewin = windowd() - (1.0-decrease_num_)*pow(windowd(),l_parameter_);
} else
decreasewin = decrease_num_ * windowd();
win = windowd();
} else {
int temp;
temp = (int)(window() / 2);
halfwin = (double) temp;
if (wnd_option_ == 6) {
/* binomial controls */
temp = (int)(window() - (1.0-decrease_num_)*pow(window(),l_parameter_));
} else
temp = (int)(decrease_num_ * window());
decreasewin = (double) temp;
win = (double) window();
}
if (how & CLOSE_SSTHRESH_HALF)
// For the first decrease, decrease by half
// even for non-standard values of decrease_num_.
if (first_decrease_ == 1 || slowstart ||
last_cwnd_action_ == CWND_ACTION_TIMEOUT) {
ssthresh_ = (int) halfwin;
} else {
ssthresh_ = (int) decreasewin;
}
else if (how & THREE_QUARTER_SSTHRESH)
if (ssthresh_ < 3*cwnd_/4)
ssthresh_ = (int)(3*cwnd_/4);
if (how & CLOSE_CWND_HALF)
// For the first decrease, decrease by half
// even for non-standard values of decrease_num_.
if (first_decrease_ == 1 || slowstart || decrease_num_ == 0.5) {
cwnd_ = halfwin;
} else cwnd_ = decreasewin;
else if (how & CWND_HALF_WITH_MIN) {
// We have not thought about how non-standard TCPs, with
// non-standard values of decrease_num_, should respond
// after quiescent periods.
cwnd_ = decreasewin;
if (cwnd_ < 1)
cwnd_ = 1;
}
else if (how & CLOSE_CWND_RESTART)
cwnd_ = int(wnd_restart_);
else if (how & CLOSE_CWND_INIT)
cwnd_ = int(wnd_init_);
else if (how & CLOSE_CWND_ONE)
cwnd_ = 1;
else if (how & CLOSE_CWND_HALF_WAY) {
// cwnd_ = win - (win - W_used)/2 ;
cwnd_ = W_used + decrease_num_ * (win - W_used);
if (cwnd_ < 1)
cwnd_ = 1;
}
if (ssthresh_ < 2)
ssthresh_ = 2;
if (how & (CLOSE_CWND_HALF|CLOSE_CWND_RESTART|CLOSE_CWND_INIT|CLOSE_CWND_ONE))
cong_action_ = TRUE;
fcnt_ = count_ = 0;
if (first_decrease_ == 1)
first_decrease_ = 0;
// ***************
/*
old_cwnd_ = (int)cwnd_;
if (old_cwnd_>1 && wnd_thru[old_cwnd_-1] >0)
last_wnd_thru = (old_cwnd_-1) * size_ *8.0/wnd_thru[old_cwnd_-1];
else
last_wnd_thru = 0;
FILE *fp;
fp = fopen("trace","a+");
fprintf(fp,"in slow down \n%f:cwnd=%f\tlast_thu=%f\told_cwnd=%d\n",
(double)Scheduler::instance().clock(),(double)cwnd_, last_wnd_thru, (int)old_cwnd_);
fclose(fp);
*/
}
/*
* Process a packet that acks previously unacknowleged data.
*/
void TcpAgent::newack(Packet* pkt)
{
double now = Scheduler::instance().clock();
hdr_tcp *tcph = hdr_tcp::access(pkt);
/*
* Wouldn't it be better to set the timer *after*
* updating the RTT, instead of *before*?
*/
newtimer(pkt);
dupacks_ = 0;
last_ack_ = tcph->seqno();
prev_highest_ack_ = highest_ack_ ;
highest_ack_ = last_ack_;
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
/*
* Update RTT only if it's OK to do so from info in the flags header.
* This is needed for protocols in which intermediate agents
* in the network intersperse acks (e.g., ack-reconstructors) for
* various reasons (without violating e2e semantics).
*/
hdr_flags *fh = hdr_flags::access(pkt);
if (!fh->no_ts_) {
if (ts_option_)
rtt_update(now - tcph->ts_echo());
if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
if (!ect_ || !ecn_backoff_ ||
!hdr_flags::access(pkt)->ecnecho()) {
/*
* Don't end backoff if still in ECN-Echo with
* a congestion window of 1 packet.
*/
t_backoff_ = 1;
ecn_backoff_ = 0;
}
rtt_active_ = 0;
if (!ts_option_)
rtt_update(now - rtt_ts_);
}
}
/* update average window */
awnd_ *= 1.0 - wnd_th_;
awnd_ += wnd_th_ * cwnd_;
if(probing_state) probing_state = 0;
}
/*
* Respond either to a source quench or to a congestion indication bit.
* This is done at most once a roundtrip time; after a source quench,
* another one will not be done until the last packet transmitted before
* the previous source quench has been ACKed.
*
* Note that this procedure is called before "highest_ack_" is
* updated to reflect the current ACK packet.
*/
void TcpAgent::ecn(int seqno)
{
if (seqno > recover_ ||
last_cwnd_action_ == CWND_ACTION_TIMEOUT) {
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_ECN;
if (cwnd_ <= 1.0) {
if (ecn_backoff_)
rtt_backoff();
else ecn_backoff_ = 1;
} else ecn_backoff_ = 0;
slowdown(CLOSE_CWND_HALF|CLOSE_SSTHRESH_HALF);
}
}
/*
* Is the connection limited by the network (instead of by a lack
* of data from the application?
*/
int TcpAgent::network_limited() {
int win = window () ;
if (t_seqno_ > (prev_highest_ack_ + win))
return 1;
else
return 0;
}
void TcpAgent::recv_newack_helper(Packet *pkt) {
//hdr_tcp *tcph = hdr_tcp::access(pkt);
newack(pkt);
if (!ect_ || !hdr_flags::access(pkt)->ecnecho() ||
(old_ecn_ && ecn_burst_)) {
/* If "old_ecn", this is not the first ACK carrying ECN-Echo
* after a period of ACKs without ECN-Echo.
* Therefore, open the congestion window. */
/* if control option is set, and the sender is not
window limited, then do not increase the window size */
if (!control_increase_ ||
(control_increase_ && (network_limited() == 1)))
opencwnd();
}
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();
}
}
/*
* Set the initial window.
*/
double
TcpAgent::initial_window()
{
//
// init_option = 1: static iw of wnd_init_
//
if (wnd_init_option_ == 1) {
return (wnd_init_);
}
else if (wnd_init_option_ == 2) {
// do iw according to Internet draft
if (size_ <= 1095) {
return (4.0);
} else if (size_ < 2190) {
return (3.0);
} else {
return (2.0);
}
}
// XXX what should we return here???
fprintf(stderr, "Wrong number of wnd_init_option_ %d\n",
wnd_init_option_);
abort();
return (2.0); // XXX make msvc happy.
}
/*
* 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 tahoe_action
* 0 0 1 tahoe_action [impossible]
* 0 1 0 tahoe_action
* 0 1 1 slow-start, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 slow-start, return
*/
/*
* A first or second duplicate acknowledgement has arrived, and
* singledup_ is enabled.
* If the receiver's advertised window permits, and we are exceeding our
* congestion window by less than numdupacks_, then send a new packet.
*/
void
TcpAgent::send_one()
{
if (t_seqno_ <= highest_ack_ + wnd_ && t_seqno_ < curseq_ &&
t_seqno_ <= highest_ack_ + cwnd_ + dupacks_ ) {
output(t_seqno_, 0);
if (QOption_)
process_qoption_after_send () ;
t_seqno_ ++ ;
// send_helper(); ??
}
return;
}
void
TcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_)) {
goto tahoe_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_CWND_ONE);
reset_rtx_timer(0,0);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
tahoe_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE);
reset_rtx_timer(0,0);
return;
}
/*
* main reception path - should only see acks, otherwise the
* network connections are misconfigured
*/
void TcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
if(tcph->reason() == TCP_REASON_ELFN)
{
probing_state = 1;
Packet::free(pkt);
return;
}
#ifdef notdef
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"received non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif
++nackpack_;
ts_peer_ = tcph->ts();
int ecnecho = hdr_flags::access(pkt)->ecnecho();
if (ecnecho && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
/* grow cwnd and check if the connection is done */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -