📄 tcp-full.cc
字号:
} last_ack_sent_ = ackno;//if (state_ != TCPS_ESTABLISHED) {//printf("%f(%s)[state:%s]: sending pkt ", now(), name(), statestr(state_));//prpkt(p);//} send(p, 0); return;}//// reset_rtx_timer: called during a retransmission timeout// to perform exponential backoff. Also, note that because// we have performed a retransmission, our rtt timer is now// invalidated (indicate this by setting rtt_active_ false)//voidFullTcpAgent::reset_rtx_timer(int /* mild */){ // cancel old timer, set a new one /* if there is no outstanding data, don't back off rtx timer * * (Fix from T. Kelly.) */ if (!(highest_ack_ == maxseq_ && restart_bugfix_)) { rtt_backoff(); // double current timeout } set_rtx_timer(); // set new timer rtt_active_ = FALSE; // no timing during this window}/* * see if we should send a segment, and if so, send it * (may be ACK or data) * return the number of data bytes sent (count a SYN or FIN as 1 each) * * simulator var, desc (name in real TCP) * -------------------------------------- * maxseq_, largest seq# we've sent plus one (snd_max) * flags_, flags regarding our internal state (t_state) * pflags, a local used to build up the tcp header flags (flags) * curseq_, is the highest sequence number given to us by "application" * highest_ack_, the highest ACK we've seen for our data (snd_una-1) * seqno, the next seq# we're going to send (snd_nxt) */intFullTcpAgent::foutput(int seqno, int reason){ // if maxseg_ not set, set it appropriately // Q: how can this happen? if (maxseg_ == 0) maxseg_ = size_ - headersize(); else size_ = maxseg_ + headersize(); int is_retransmit = (seqno < maxseq_); int quiet = (highest_ack_ == maxseq_); int pflags = outflags(); int syn = (seqno == iss_); int emptying_buffer = FALSE; int buffered_bytes = (infinite_send_) ? TCP_MAXSEQ : curseq_ - highest_ack_ + 1; int win = window() * maxseg_; // window (in bytes) int off = seqno - highest_ack_; // offset of seg in window int datalen; //int amtsent = 0; // be careful if we have not received any ACK yet if (highest_ack_ < 0) { if (!infinite_send_) buffered_bytes = curseq_ - iss_;; off = seqno - iss_; } if (syn && !data_on_syn_) datalen = 0; else if (pipectrl_) datalen = buffered_bytes - off; else datalen = min(buffered_bytes, win) - off; if ((signal_on_empty_) && (!buffered_bytes) && (!syn)) bufferempty(); // // in real TCP datalen (len) could be < 0 if there was window // shrinkage, or if a FIN has been sent and neither ACKd nor // retransmitted. Only this 2nd case concerns us here... // if (datalen < 0) { datalen = 0; } else if (datalen > maxseg_) { datalen = maxseg_; } // // this is an option that causes us to slow-start if we've // been idle for a "long" time, where long means a rto or longer // the slow-start is a sort that does not set ssthresh // if (slow_start_restart_ && quiet && datalen > 0) { if (idle_restart()) { slowdown(CLOSE_CWND_INIT); } } // // see if sending this packet will empty the send buffer // a dataless SYN packet counts also // if (!infinite_send_ && ((seqno + datalen) > curseq_ || (syn && datalen == 0))) { emptying_buffer = TRUE; // // if not a retransmission, notify application that // everything has been sent out at least once. // if (!syn) { idle(); if (close_on_empty_ && quiet) { flags_ |= TF_NEEDCLOSE; } } pflags |= TH_PUSH; // // if close_on_empty set, we are finished // with this connection; close it // } else { /* not emptying buffer, so can't be FIN */ pflags &= ~TH_FIN; } if (infinite_send_ && (syn && datalen == 0)) pflags |= TH_PUSH; // set PUSH for dataless SYN /* sender SWS avoidance (Nagle) */ if (datalen > 0) { // if full-sized segment, ok if (datalen == maxseg_) goto send; // if Nagle disabled and buffer clearing, ok if ((quiet || nodelay_) && emptying_buffer) goto send; // if a retransmission if (is_retransmit) goto send; // if big "enough", ok... // (this is not a likely case, and would // only happen for tiny windows) if (datalen >= ((wnd_ * maxseg_) / 2.0)) goto send; } if (need_send()) goto send; /* * send now if a control packet or we owe peer an ACK * TF_ACKNOW can be set during connection establishment and * to generate acks for out-of-order data */ if ((flags_ & (TF_ACKNOW|TF_NEEDCLOSE)) || (pflags & (TH_SYN|TH_FIN))) { goto send; } /* * No reason to send a segment, just return. */ return 0;send: // is a syn or fin? syn = (pflags & TH_SYN) ? 1 : 0; int fin = (pflags & TH_FIN) ? 1 : 0; /* setup ECN syn and ECN SYN+ACK packet headers */ if (ecn_ && syn && !(pflags & TH_ACK)){ pflags |= TH_ECE; pflags |= TH_CWR; } if (ecn_ && syn && (pflags & TH_ACK)){ pflags |= TH_ECE; pflags &= ~TH_CWR; } else if (ecn_ && ect_ && cong_action_ && (!is_retransmit || SetCWRonRetransmit_)) /* * Don't set CWR for a retranmitted SYN+ACK (has ecn_ * and cong_action_ set) or on any retransmits. * -M. Weigle 6/19/02 */ /* set CWR if necessary */ pflags |= TH_CWR; /* moved from sendpacket() -M. Weigle 6/19/02 */ // // although CWR bit is ordinarily associated with ECN, // it has utility within the simulator for traces. Thus, set // it even if we aren't doing ECN // if (datalen > 0 && cong_action_ && !is_retransmit) { pflags |= TH_CWR; } /* set ECE if necessary */ if (ecn_ && ect_ && recent_ce_ ) pflags |= TH_ECE; /* * Tack on the FIN flag to the data segment if close_on_empty_ * was previously set-- avoids sending a separate FIN */ if (flags_ & TF_NEEDCLOSE) { flags_ &= ~TF_NEEDCLOSE; if (state_ <= TCPS_ESTABLISHED && state_ != TCPS_CLOSED) { pflags |=TH_FIN; fin = 1; /* FIN consumes sequence number */ newstate(TCPS_FIN_WAIT_1); } } sendpacket(seqno, rcv_nxt_, pflags, datalen, reason); /* * Data sent (as far as we can tell). * Any pending ACK has now been sent. */ flags_ &= ~(TF_ACKNOW|TF_DELACK); /* * if we have reacted to congestion recently, the * slowdown() procedure will have set cong_action_ and * sendpacket will have copied that to the outgoing pkt * CWR field. If that packet contains data, then * it will be reliably delivered, so we are free to turn off the * cong_action_ state now If only a pure ACK, we keep the state * around until we actually send a segment */ int reliable = datalen + syn + fin; // seq #'s reliably sent /* * Don't reset cong_action_ until we send new data. * -M. Weigle 6/19/02 */ if (cong_action_ && reliable > 0 && !is_retransmit) cong_action_ = FALSE; // highest: greatest sequence number sent + 1 // and adjusted for SYNs and FINs which use up one number int highest = seqno + reliable; if (highest > maxseq_) { maxseq_ = highest; // // if we are using conventional RTT estimation, // establish timing on this segment // if (!ts_option_ && rtt_active_ == FALSE) { rtt_active_ = TRUE; // set timer rtt_seq_ = seqno; // timed seq # rtt_ts_ = now(); // when set } } /* * Set retransmit timer if not currently set, * and not doing an ack or a keep-alive probe. * Initial value for retransmit timer is smoothed * round-trip time + 2 * round-trip time variance. * Future values are rtt + 4 * rttvar. */ if (rtx_timer_.status() != TIMER_PENDING && reliable) { set_rtx_timer(); // no timer pending, schedule one } return (reliable);}/* * * send_much: send as much data as we are allowed to. This is * controlled by the "pipectrl_" variable. If pipectrl_ is set * to FALSE, then we are working as a normal window-based TCP and * we are allowed to send whatever the window allows. * If pipectrl_ is set to TRUE, then we are allowed to send whatever * pipe_ allows us to send. One tricky part is to make sure we * do not overshoot the receiver's advertised window if we are * in (pipectrl_ == TRUE) mode. */ voidFullTcpAgent::send_much(int force, int reason, int maxburst){ int npackets = 0; // sent so far//if ((int(t_seqno_)) > 1)//printf("%f: send_much(f:%d, win:%d, pipectrl:%d, pipe:%d, t_seqno:%d, topwin:%d, maxseq_:%d\n",//now(), force, win, pipectrl_, pipe_, int(t_seqno_), topwin, int(maxseq_)); if (!force && (delsnd_timer_.status() == TIMER_PENDING)) return; while (1) { /* * note that if output decides to not actually send * (e.g. because of Nagle), then if we don't break out * of this loop, we can loop forever at the same * simulated time instant */ int amt; int seq = nxt_tseq(); if (!force && !send_allowed(seq)) break; // Q: does this need to be here too? if (!force && overhead_ != 0 && (delsnd_timer_.status() != TIMER_PENDING)) { delsnd_timer_.resched(Random::uniform(overhead_)); return; } if ((amt = foutput(seq, reason)) <= 0) break; if ((outflags() & TH_FIN)) --amt; // don't count FINs sent(seq, amt); force = 0; if ((outflags() & (TH_SYN|TH_FIN)) || (maxburst && ++npackets >= maxburst)) break; } return;}/* * base TCP: we are allowed to send a sequence number if it * is in the window */intFullTcpAgent::send_allowed(int seq){ int win = window() * maxseg_; int topwin = curseq_; // 1 seq number past the last byte we can send if ((topwin > highest_ack_ + win) || infinite_send_) topwin = highest_ack_ + win; return (seq < topwin);}/* * Process an ACK * this version of the routine doesn't necessarily * require the ack to be one which advances the ack number * * if this ACKs a rtt estimate * indicate we are not timing * reset the exponential timer backoff (gamma) * update rtt estimate * cancel retrans timer if everything is sent and ACK'd, else set it * advance the ack number if appropriate * update segment to send next if appropriate */voidFullTcpAgent::newack(Packet* pkt){ hdr_tcp *tcph = hdr_tcp::access(pkt); register int ackno = tcph->ackno(); int progress = (ackno > highest_ack_); if (ackno == maxseq_) { cancel_rtx_timer(); // all data ACKd } else if (progress) { set_rtx_timer(); } // advance the ack number if this is for new data if (progress) highest_ack_ = ackno; // if we have suffered a retransmit timeout, t_seqno_ // will have been reset to highest_ ack. If the // receiver has cached some data above t_seqno_, the // new-ack value could (should) jump forward. We must // update t_seqno_ here, otherwise we would be doing // go-back-n. if (t_seqno_ < highest_ack_) t_seqno_ = highest_ack_; // seq# to send next /* * 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_) { recent_age_ = now(); recent_ = tcph->ts(); 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; } } else if (rtt_active_ && ackno > rtt_seq_) { // got an RTT sample, record it // "t_backoff_ = 1;" deleted by T. Kelly. rtt_active_ = FALSE; rtt_update(now() - rtt_ts_); } 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. * Fix from T. Kelly. */ t_backoff_ = 1; ecn_backoff_ = 0; } } return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -