📄 tcp.cc
字号:
double TcpAgent::rtt_timeout(){ double timeout; if (rfc2988_) { // Correction from Tom Kelly to be RFC2988-compliant, by // clamping minrto_ before applying t_backoff_. if (t_rtxcur_ < minrto_) timeout = minrto_ * t_backoff_; else timeout = t_rtxcur_ * t_backoff_; } else { timeout = t_rtxcur_ * t_backoff_; if (timeout < minrto_) timeout = minrto_; } if (timeout > maxrto_) timeout = maxrto_; if (timeout < 2.0 * tcp_tick_) { if (timeout < 0) { fprintf(stderr, "TcpAgent: negative RTO! (%f)\n", timeout); exit(1); } timeout = 2.0 * tcp_tick_; } return (timeout);}/* This has been modified to use the tahoe code. */void TcpAgent::rtt_update(double tao){ 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_; return;}void TcpAgent::rtt_backoff(){ if (t_backoff_ < 64) t_backoff_ <<= 1; 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; }}/* * headersize: * how big is an IP+TCP header in bytes; include options such as ts * this function should be virtual so others (e.g. SACK) can override */int TcpAgent::headersize(){ int total = tcpip_base_hdr_size_; if (total < 1) { fprintf(stderr, "TcpAgent(%s): warning: tcpip hdr size is only %d bytes\n", name(), tcpip_base_hdr_size_); } if (ts_option_) total += ts_option_size_; return (total);}void TcpAgent::output(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); hdr_ip *iph = hdr_ip::access(p); int databytes = hdr_cmn::access(p)->size(); tcph->seqno() = seqno; tcph->ts() = Scheduler::instance().clock(); tcph->ts_echo() = ts_peer_; tcph->reason() = reason; tcph->last_rtt() = int(int(t_rtt_)*tcp_tick_*1000); 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_) { 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; } if (qs_enabled_) { hdr_qs *qsh = hdr_qs::access(p); if (rate_request_ > 0) { // QuickStart code from Srikanth Sundarrajan. qsh->flag() = QS_REQUEST; Random::seed_heuristically(); qsh->ttl() = Random::integer(256); ttl_diff_ = (iph->ttl() - qsh->ttl()) % 256; qsh->rate() = rate_request_; qs_requested_ = 1; } else { qsh->flag() = QS_DISABLE; } } } else if (useHeaders_ == true) { hdr_cmn::access(p)->size() += headersize(); } 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_ += databytes; 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_ += databytes; } 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); } if (strcmp(argv[1], "eventtrace") == 0) { et_ = (EventTrace *)TclObject::lookup(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){ 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_ ++ ; if (qs_approved_ == 1) { // delay = effective RTT / window double delay = (double) t_rtt_ * tcp_tick_ / win; delsnd_timer_.resched(delay); return; } } 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();}/* * for experimental, high-speed TCP */double TcpAgent::linear(double x, double x_1, double y_1, double x_2, double y_2){ // The y coordinate factor ranges from y_1 to y_2 // as the x coordinate ranges from x_1 to x_2. double y = y_1 + ((y_2 - y_1) * ((x - x_1)/(x_2-x_1))); return y;}/* * Limited Slow-Start for large congestion windows. * This is only used when max_ssthresh_ is non-zero. */double TcpAgent::limited_slow_start(double cwnd, double max_ssthresh, double increment){ int round = int(cwnd / (double(max_ssthresh)/2.0)); double increment1 = 1.0/(double(round)); if (increment < increment1) increment = increment1; return increment;}/* * For retrieving numdupacks_. */int TcpAgent::numdupacks(double cwnd){ int cwndfraction = (int) cwnd/numdupacksFrac_; if (numdupacks_ > cwndfraction) { return numdupacks_; } else { return cwndfraction; }}/* * Calculating the packet drop rate p for highspeed TCP. */double TcpAgent::compute_p(){ double p; double low_p = 1.5/(low_window_*low_window_); p = exp(linear(log(cwnd_), log(low_window_), log(low_p), log(high_window_), log(high_p_))); return p;}/* * Calculating the decrease parameter for highspeed TCP. */double TcpAgent::decrease_param(){ double decrease; decrease = linear(log(cwnd_), log(low_window_), 0.5, log(high_window_), high_decrease_); return decrease;}/* * Calculating the increase parameter for highspeed TCP. */double TcpAgent::increase_param(){ double increase, decrease, p, answer; /* extending the slow-start for high-speed TCP */ /* for highspeed TCP -- from Sylvia Ratnasamy, */ /* modifications by Sally Floyd and Evandro de Souza */ // p ranges from 1.5/W^2 at congestion window low_window_, to // high_p_ at congestion window high_window_, on a log-log scale. // The decrease factor ranges from 0.5 to high_decrease // as the window ranges from low_window to high_window, // as the log of the window. // For an efficient implementation, this would just be looked up // in a table, with the increase and decrease being a function of the // congestion window. if (cwnd_ <= low_window_) { answer = 1 / cwnd_; return answer; } else if (cwnd_ >= cwnd_last_ && cwnd_ < cwnd_frac_ * cwnd_last_ ) { answer = increase_last_ / cwnd_; return answer; } else { p = compute_p(); decrease = decrease_param(); increase = (cwnd_ * cwnd_ *2.0* decrease * p)/(2.0 - decrease); // double max_increase = 157.8; // if (increase > max_increase) { // increase = max_increase; // } answer = increase / cwnd_; cwnd_last_ = cwnd_; increase_last_ = increase; return answer; } }/* * open up the congestion window */void TcpAgent::opencwnd(){ double increment; double f; double fold; double diff; double RTT_Scaling; double now=Scheduler::instance().clock();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -