📄 tcp.cc
字号:
fprintf(stderr, "TcpAgent: negative RTO! (%f)\n",
timeout);
exit(1);
}
timeout = 2 * tcp_tick_;
}
return (timeout);
}
/* This has been modified to use the tahoe code. */
void TcpAgent::rtt_update(double tao)
{
int i;
if(!rtt_inited)
{
t_myrtt = tao;
rtt_inited = 1;
for(i=0;i<256;i++)
wnd_thru[i] = 0.0;
//ssthresh_ = 1;
//increase_num_ = 0.5;
}
else
{
t_myrtt = t_myrtt*0.875 + tao*0.125;
/*increase_num_ = wnd_thru[window()] * 4;
if(increase_num_ > 1) increase_num_ = 1;
if(increase_num_ < 0.3) increase_num_ = 0.3; */
};
double now = Scheduler::instance().clock();
if (ts_option_)
t_rtt_ = int(tao /tcp_tick_ + 0.5);
else {
double sendtime = now - tao;
sendtime += boot_time_;
double tickoff = fmod(sendtime, tcp_tick_);
t_rtt_ = int((tao + tickoff) / tcp_tick_);
}
if (t_rtt_ < 1)
t_rtt_ = 1;
//
// srtt has 3 bits to the right of the binary point
// rttvar has 2
//
if (t_srtt_ != 0) {
register short delta;
delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS); // d = (m - a0)
if ((t_srtt_ += delta) <= 0) // a1 = 7/8 a0 + 1/8 m
t_srtt_ = 1;
if (delta < 0)
delta = -delta;
delta -= (t_rttvar_ >> T_RTTVAR_BITS);
if ((t_rttvar_ += delta) <= 0) // var1 = 3/4 var0 + 1/4 |d|
t_rttvar_ = 1;
} else {
t_srtt_ = t_rtt_ << T_SRTT_BITS; // srtt = rtt
t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1); // rttvar = rtt / 2
}
//
// Current retransmit value is
// (unscaled) smoothed round trip estimate
// plus 2^rttvar_exp_ times (unscaled) rttvar.
//
t_rtxcur_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS))) +
t_srtt_) >> T_SRTT_BITS ) * tcp_tick_;
if(wnd_thru[window()] == 0)
wnd_thru[window()] = tao;
else
wnd_thru[window()] = wnd_thru[window()]*0.875 + tao*0.125;
return;
}
void TcpAgent::rtt_backoff()
{
if (t_backoff_ < 64)
t_backoff_ <<= 1;
// disable the rtt_backoff for test only, Zhenghua
;
if (t_backoff_ > 8) {
/*
* If backed off this far, clobber the srtt
* value, storing it in the mean deviation
* instead.
*/
t_rttvar_ += (t_srtt_ >> T_SRTT_BITS);
t_srtt_ = 0;
}
}
void TcpAgent::output(int seqno, int reason)
/* just a stub for recording outgoing packets here... */
{
output_real(seqno, reason);
return;
psn[wptr % 256] = seqno;
preason[wptr % 256] = reason;
wptr = wptr + 1 ;
FILE *fp;
fp = fopen("pace","a+");
if(!(pace_timer_.status() == TIMER_PENDING))
{
fprintf(fp,"at output timer not pending ...\n%f\t%d\t%d\n",Scheduler::instance().clock(),wptr,rptr);
output_paced();
}
else
fprintf(fp,"at output timer pending...\n%f\t%d\t%d\n",Scheduler::instance().clock(),wptr,rptr);
fclose(fp);
}
void TcpAgent::output_paced(void)
{
FILE *fp;
fp = fopen("pace","a+");
// if no packet to send, return
if (rptr >= wptr)
{
fprintf(fp,"at output_paced, nothing to send, at %f\t%d\t%d\n",Scheduler::instance().clock(),wptr,rptr);
fclose(fp);
return;
}
output_real(psn[rptr % 256], preason[rptr % 256]);
rptr ++;
fprintf(fp,"at output_paced, sent one, and setting timer...at %f\t%d\t%d\n",Scheduler::instance().clock(),wptr,rptr);
set_pace_timer();
fclose(fp);
}
void TcpAgent::set_pace_timer()
{
double t;
// t = ((t_srtt_ >> T_SRTT_BITS) * tcp_tick_) / window();
//t = t_myrtt / (double) window();
t = wnd_thru[window()] / (double) window();
if(t0>2 || t0<=0)
t0 = ((size_ + 320.0)*8.0 / (double)2000000.0 + 2.0 *0.0009125) *3.0;
if (t < t0) t=t0;
FILE *fp;
fp = fopen("pace","a+");
fprintf(fp,"%f\t%f\t%f\t%6.3f\t%d\t%d\t%d\n", Scheduler::instance().clock(), t,t0,
t_myrtt, window(),wptr,rptr);
fclose(fp);
pace_timer_.resched(t);
}
void TcpAgent::output_real(int seqno, int reason)
{
int force_set_rtx_timer = 0;
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_flags* hf = hdr_flags::access(p);
tcph->seqno() = seqno;
tcph->ts() = Scheduler::instance().clock();
tcph->ts_echo() = ts_peer_;
tcph->reason() = reason;
if (ecn_) {
hf->ect() = 1; // ECN-capable transport
}
if (cong_action_) {
hf->cong_action() = TRUE; // Congestion action.
cong_action_ = FALSE;
}
/* Check if this is the initial SYN packet. */
if (seqno == 0) {
if (syn_) {
hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
}
if (ecn_) {
hf->ecnecho() = 1;
// hf->cong_action() = 1;
hf->ect() = 0;
}
}
if(probing_state) hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
int bytes = hdr_cmn::access(p)->size();
/* if no outstanding data, be sure to set rtx timer again */
if (highest_ack_ == maxseq_)
force_set_rtx_timer = 1;
/* call helper function to fill in additional fields */
output_helper(p);
++ndatapack_;
ndatabytes_ += bytes;
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_ = Scheduler::instance().clock();
}
}
} else {
++nrexmitpack_;
nrexmitbytes_ += bytes;
}
if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer)
/* No timer pending. Schedule one. */
set_rtx_timer();
}
/*
* Must convert bytes into packets for one-way TCPs.
* If nbytes == -1, this corresponds to infinite send. We approximate
* infinite by a very large number (TCP_MAXSEQ).
*/
void TcpAgent::sendmsg(int nbytes, const char* /*flags*/)
{
if (nbytes == -1 && curseq_ <= TCP_MAXSEQ)
curseq_ = TCP_MAXSEQ;
else
curseq_ += (nbytes/size_ + (nbytes%size_ ? 1 : 0));
send_much(0, 0, maxburst_);
}
void TcpAgent::advanceby(int delta)
{
curseq_ += delta;
if (delta > 0)
closed_ = 0;
send_much(0, 0, maxburst_);
}
int TcpAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "advance") == 0) {
int newseq = atoi(argv[2]);
if (newseq > maxseq_)
advanceby(newseq - curseq_);
else
advanceby(maxseq_ - curseq_);
return (TCL_OK);
}
if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
/*
* Curtis Villamizar's trick to transfer tcp connection
* parameters to emulate http persistent connections.
*
* Another way to do the same thing is to open one tcp
* object and use start/stop/maxpkts_ or advanceby to control
* how much data is sent in each burst.
* With a single connection, slow_start_restart_
* should be configured as desired.
*
* This implementation (persist) may not correctly
* emulate pure-BSD-based systems which close cwnd
* after the connection goes idle (slow-start
* restart). See appendix C in
* Jacobson and Karels ``Congestion
* Avoidance and Control'' at
* <ftp://ftp.ee.lbl.gov/papers/congavoid.ps.Z>
* (*not* the original
* '88 paper) for why BSD does this. See
* ``Performance Interactions Between P-HTTP and TCP
* Implementations'' in CCR 27(2) for descriptions of
* what other systems do the same.
*
*/
if (strcmp(argv[1], "persist") == 0) {
TcpAgent *other
= (TcpAgent*)TclObject::lookup(argv[2]);
cwnd_ = other->cwnd_;
awnd_ = other->awnd_;
ssthresh_ = other->ssthresh_;
t_rtt_ = other->t_rtt_;
t_srtt_ = other->t_srtt_;
t_rttvar_ = other->t_rttvar_;
t_backoff_ = other->t_backoff_;
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
int TcpAgent::window()
{
return (cwnd_ < wnd_ ? (int)cwnd_ : (int)wnd_);
}
double TcpAgent::windowd()
{
return (cwnd_ < wnd_ ? (double)cwnd_ : (double)wnd_);
}
/*
* Try to send as much data as the window will allow. The link layer will
* do the buffering; we ask the application layer for the size of the packets.
*/
void TcpAgent::send_much(int force, int reason, int maxburst)
{
char ch[20], ch2[10];
strcpy(ch,"wnd");
sprintf(ch2,"%d",nodeid_);
strcat(ch,ch2);
// Add by Zhenghua to peek the cwnd_
FILE *fp;
//fprintf(fp,"%f\t%f\n",Scheduler::instance().clock(), double(cwnd_));
//fclose(fp);
fp=fopen(ch,"a+");
fprintf(fp,"%f\t%d\n",Scheduler::instance().clock(), window());
fclose(fp);
send_idle_helper();
int win = window();
int npackets = 0;
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();
if (burstsnd_timer_.status() == TIMER_PENDING)
return;
while (t_seqno_ <= highest_ack_ + win && t_seqno_ < curseq_) {
if (overhead_ == 0 || force) {
output(t_seqno_, reason);
npackets++;
if (QOption_)
process_qoption_after_send () ;
t_seqno_ ++ ;
} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
/*
* Set a delayed send timeout.
*/
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
win = window();
if (maxburst && npackets == maxburst)
break;
}
/* call helper function */
send_helper(maxburst);
}
/*
* We got a timeout or too many duplicate acks. Clear the retransmit timer.
* Resume the sequence one past the last packet acked.
* "mild" is 0 for timeouts and Tahoe dup acks, 1 for Reno dup acks.
* "backoff" is 1 if the timer should be backed off, 0 otherwise.
*/
void TcpAgent::reset_rtx_timer(int mild, int backoff)
{
if (backoff)
rtt_backoff();
set_rtx_timer();
if (!mild)
t_seqno_ = highest_ack_ + 1;
rtt_active_ = 0;
}
/*
* Set retransmit timer using current rtt estimate. By calling resched(),
* it does not matter whether the timer was already running.
*/
void TcpAgent::set_rtx_timer()
{
rtx_timer_.resched(rtt_timeout());
}
/*
* Set new retransmission timer if not all outstanding
* or available data acked, or if we are unable to send because
* cwnd is less than one (as when the ECN bit is set when cwnd was 1).
* Otherwise, if a timer is still outstanding, cancel it.
*/
void TcpAgent::newtimer(Packet* pkt)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
/*
* t_seqno_, the next packet to send, is reset (decreased)
* to highest_ack_ + 1 after a timeout,
* so we also have to check maxseq_, the highest seqno sent.
* In addition, if the packet sent after the timeout has
* the ECN bit set, then the returning ACK caused cwnd_ to
* be decreased to less than one, and we can't send another
* packet until the retransmit timer again expires.
* So we have to check for "cwnd_ < 1" as well.
*/
if (t_seqno_ > tcph->seqno() || tcph->seqno() < maxseq_ || cwnd_ < 1)
set_rtx_timer();
else
cancel_rtx_timer();
}
/*
* open up the congestion window
*/
void TcpAgent::opencwnd()
{
double cur_thu;
int save_win = window();
int i;
if (cwnd_ < ssthresh_) {
/* slow-start (exponential) */
cwnd_ += 1;
} else {
/* linear */
double f;
switch (wnd_option_) {
case 0:
if (++count_ >= cwnd_) {
count_ = 0;
++cwnd_;
}
break;
case 1:
/* This is the standard algorithm. */
cwnd_ += increase_num_ / cwnd_;
break;
case 2:
/* These are window increase algorithms
* for experimental purposes only. */
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -