📄 tcp.cc
字号:
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; // for event tracing slow start if (cwnd_ == 1 || slowstart) // Not sure if this is best way to capture slow_start // This is probably tracing a superset of slowdowns of // which all may not be slow_start's --Padma, 07/'01. trace_event("SLOW_START"); }/* * 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*? */ if (!timerfix_) 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_) { ts_echo_=tcph->ts_echo(); rtt_update(now - tcph->ts_echo()); if (ts_resetRTO_ && (!ect_ || !ecn_backoff_ || !hdr_flags::access(pkt)->ecnecho())) { // From Andrei Gurtov /* * Don't end backoff if still in ECN-Echo with * a congestion window of 1 packet. */ t_backoff_ = 1; ecn_backoff_ = 0; } } 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_); } } if (timerfix_) newtimer(pkt); /* update average window */ awnd_ *= 1.0 - wnd_th_; awnd_ += wnd_th_ * cwnd_;}/* * 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); ++necnresponses_ ; // added by sylvia to count number of ecn responses }}/* * 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 (qs_window_ && highest_ack_ >= qs_window_) { // All segments in the QS window have been acknowledged. // We can exit the Quick-Start phase. qs_window_ = 0; } 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(); } if (frto_ == 1) { /* * New ack after RTO. If F-RTO is enabled, try to transmit new * previously unsent segments. * If there are no new data or receiver window limits the * transmission, revert to traditional recovery. */ if (recover_ + 1 >= highest_ack_ + wnd_ || recover_ + 1 >= curseq_) { frto_ = 0; } else if (highest_ack_ == recover_) { /* * F-RTO step 2a) RTO retransmission fixes whole * window => cancel F-RTO */ frto_ = 0; } else { t_seqno_ = recover_ + 1; frto_ = 2; } } else if (frto_ == 2) { /* * Second new ack after RTO. If F-RTO is enabled, RTO can be * declared spurious */ spurious_timeout(); }}/* * Set the initial window. */doubleTcpAgent::initial_window(){ // If Quick-Start Request was approved, use that as a basis for // initial window if (qs_cwnd_) { return (qs_cwnd_); } // // 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. */voidTcpAgent::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;}voidTcpAgent::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_; if (!lossQuickStart()) { // we are now going to fast-retransmit and willtrace that event trace_event("FAST_RETX"); last_cwnd_action_ = CWND_ACTION_DUPACK; slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE); } reset_rtx_timer(0,0); return;}/* * When exiting QuickStart, reduce the congestion window to the * size that was actually used. */void TcpAgent::endQuickStart(){ qs_approved_ = 0; qs_cwnd_ = 0; qs_window_ = maxseq_; int new_cwnd = maxseq_ - last_ack_; if (new_cwnd > 1 && new_cwnd < cwnd_) { cwnd_ = new_cwnd; if (cwnd_ < initial_window()) cwnd_ = initial_window(); }}void TcpAgent::processQuickStart(Packet *pkt){ // QuickStart code from Srikanth Sundarrajan. hdr_tcp *tcph = hdr_tcp::access(pkt); hdr_qs *qsh = hdr_qs::access(pkt); double now = Scheduler::instance().clock(); int app_rate; // printf("flag: %d ttl: %d ttl_diff: %d rate: %d\n", qsh->flag(), // qsh->ttl(), ttl_diff_, qsh->rate()); qs_requested_ = 0; qs_approved_ = 0; if (qsh->flag() == QS_RESPONSE && qsh->ttl() == ttl_diff_ && qsh->rate() > 0) { app_rate = (int) (hdr_qs::rate_to_Bps(qsh->rate()) * (now - tcph->ts_echo()) / (size_ + headersize()));#ifdef QS_DEBUG printf("Quick Start approved, rate %d, window %d\n", qsh->rate(), app_rate);#endif if (app_rate > initial_window()) { qs_cwnd_ = app_rate; qs_approved_ = 1; } } else { // Quick Start rejected#ifdef QS_DEBUG printf("Quick Start rejected\n");#endif }}/* * ACK has been received, hook from recv() */void TcpAgent::recv_frto_helper(Packet *pkt){ hdr_tcp *tcph = hdr_tcp::access(pkt); if (tcph->seqno() == last_ack_ && frto_ != 0) { /* * Duplicate ACK while in F-RTO indicates that the * timeout was valid. Go to slow start retransmissions. */ t_seqno_ = highest_ack_ + 1; cwnd_ = frto_; frto_ = 0; // Must zero dupacks (in order to trigger send_much at recv) // dupacks is increased in recv after exiting this function dupacks_ = -1; }}/* * A spurious timeout has been detected. Do appropriate actions. */void TcpAgent::spurious_timeout(){ frto_ = 0; switch (spurious_response_) { case 1: default: /* * Full revert of congestion window * (FlightSize before last acknowledgment) */ cwnd_ = t_seqno_ - prev_highest_ack_; break; case 2: /* * cwnd = reduced ssthresh (approx. half of the earlier pipe) */ cwnd_ = ssthresh_; break; case 3: /* * slow start, but without retransmissions */ cwnd_ = 1; break; } /* * Revert ssthresh to size before retransmission timeout */ ssthresh_ = pipe_prev_; /* If timeout was spurious, bugfix is not needed */ recover_ = highest_ack_ - 1;}/* * Loss occurred in Quick-Start window. * If Quick-Start is enabled, packet loss in the QS phase should * trigger slow start instead of the regular fast retransmit, * see [draft-amit-quick-start-03.txt] (to appear). * We use variable tcp_qs_recovery_ to toggle this behaviour on and off. * If tcp_qs_recovery_ is true, initiate slow start to probe for * a correct window size. * * Return value: non-zero if Quick-Start specific loss recovery took place */int TcpAgent::lossQuickStart(){ if (qs_window_ && tcp_qs_recovery_) { //recover_ = maxseq_; //reset_rtx_timer(1,0); slowdown(CLOSE_CWND_INIT); // reset ssthresh to half of W-D/2? qs_window_ = 0; output(last_ack_ + 1, TCP_REASON_DUPACK); return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -