⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp-qs.cc

📁 Ns2 TCP 协议改进 版本 提高goodput
💻 CC
字号:
/* * tcp-qs.cc * Copyright (C) 2001 by the University of Southern California * $Id: tcp-qs.cc,v 1.8 2005/08/25 18:58:12 johnh Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * * The copyright of this module includes the following * linking-with-specific-other-licenses addition: * * In addition, as a special exception, the copyright holders of * this module give you permission to combine (via static or * dynamic linking) this module with free software programs or * libraries that are released under the GNU LGPL and with code * included in the standard release of ns-2 under the Apache 2.0 * license or under otherwise-compatible licenses with advertising * requirements (or modified versions of such code, with unchanged * license).  You may copy and distribute such a system following the * terms of the GNU GPL for this module and the licenses of the * other code concerned, provided that you include the source code of * that other code when and as the GNU GPL requires distribution of * source code. * * Note that people who make modified versions of this module * are not obligated to grant this special exception for their * modified versions; it is their choice whether to do so.  The GNU * General Public License gives permission to release a modified * version without this exception; this exception also makes it * possible to release a modified version which carries forward this * exception. * *//* * Quick Start for TCP and IP. * A scheme for transport protocols to dynamically determine initial  * congestion window size. * * http://www.ietf.org/internet-drafts/draft-amit-quick-start-02.ps * * This implements the TCP Quick Start Source and Sink Agents. * TCP Quick Start Source Agent is based on the Rate Based  * implementation of TCP. "Agent/TCP/Newreno/QS", "Agent/TCPSink/QS" * * tcp-qs.cc * * Srikanth Sundarrajan, 2002 * sundarra@usc.edu * * Modifications: Pasi Sarolahti <pasi.sarolahti@iki.fi>, Sept. 2004 */#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include "ip.h"#include "tcp.h"#include "flags.h"#include "hdr_qs.h"#include "random.h"#include "tcp-sink.h"#ifndef MIN#define MIN(x, y) ((x)<(y) ? (x) : (y))#endif /* ! MIN */#if 0#define RBP_DEBUG_PRINTF(x) printf x#else /* ! 0 */#define RBP_DEBUG_PRINTF(x)#endif /* 0 */#define RBP_MIN_SEGMENTS 2/*********************************************************************** * * The New reno-based version * */class QSNewRenoTcpAgent;class QSNewRenoPaceTimer : public TimerHandler {public:	QSNewRenoPaceTimer(QSNewRenoTcpAgent *a) : TimerHandler() { a_ = a; }protected:	virtual void expire(Event *e);	QSNewRenoTcpAgent *a_;};// Hmmm... ``a is a'' in the construction of the QSNewRenoPaceTimer edifice :->class QSNewRenoTcpAgent : public virtual NewRenoTcpAgent {	friend class QSNewRenoPaceTimer; public:	QSNewRenoTcpAgent();	virtual void recv(Packet *pkt, Handler *);	virtual void timeout(int tno);	virtual void send_much(int force, int reason, int maxburst);	virtual void output(int force, int reason);	double rbp_scale_;   // conversion from actual -> rbp send rates	// enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };	// int rbp_rate_algorithm_;protected:	void paced_send_one();	int able_to_rbp_send_one();	// stats on what we did	int rbp_segs_actually_paced_;	int ttl_diff_;	int qs_approved_;	int rate_request_;	int session_id_;	static int next_flow_;	enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };	enum rbp_modes rbp_mode_;	double rbp_inter_pace_delay_;	QSNewRenoPaceTimer pace_timer_;};int QSNewRenoTcpAgent::next_flow_ = 0;static class QSNewRenoTcpClass : public TclClass {public:	QSNewRenoTcpClass() : TclClass("Agent/TCP/Newreno/QS") {}	TclObject* create(int, const char*const*) {		return (new QSNewRenoTcpAgent());	}} class_newreno_qs;void QSNewRenoPaceTimer::expire(Event *) { a_->paced_send_one(); }QSNewRenoTcpAgent::QSNewRenoTcpAgent() : TcpAgent(),	ttl_diff_(0), qs_approved_(0), rbp_mode_(RBP_OFF), pace_timer_(this){	bind("rbp_scale_", &rbp_scale_);	// algorithm is not used in New Reno	// bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);	bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);	bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);	bind("rate_request_", &rate_request_);	session_id_ = next_flow_ % 32;	next_flow_ = session_id_ + 1;}voidQSNewRenoTcpAgent::recv(Packet *pkt, Handler *hand){	double now = Scheduler::instance().clock();	int app_rate;	hdr_tcp *tcph = hdr_tcp::access(pkt);	if (rbp_mode_ != RBP_OFF) {		// reciept of anything disables rbp		rbp_mode_ = RBP_OFF;		// reset cwnd such that we're now ack clocked.		if (tcph->seqno() > last_ack_) {			cwnd_ = maxseq_ - last_ack_; //this is what we need for QS.			RBP_DEBUG_PRINTF(("\ncwnd-after-first-ack=%g\n", (double)cwnd_));		};	};	if (acked_ == 0) {		hdr_qs *qsh = hdr_qs::access(pkt);		if (qsh->flag() == QS_RESPONSE && qsh->ttl() == ttl_diff_ && qsh->rate() > 0) {			printf("Quick Start approved\t");                        // PS: Convert rate to initial window in pkts                        app_rate = (int) (hdr_qs::rate_to_Bps(qsh->rate()) *                            (now - tcph->ts_echo()) / (size_ + headersize()));			if (app_rate > initial_window()) {				rbp_mode_ = RBP_POSSIBLE;				wnd_init_option_ = 1;				wnd_init_ = app_rate;				printf("%d: rate= %d, rtt = %f\n", addr(), app_rate, (now - tcph->ts_echo()));				qs_approved_ = 1;			}			else {				printf("%d: quick start approved, but rate too low %d, fall-back to slow start\n", addr(), app_rate);				rbp_mode_ = RBP_OFF;				qsh->flag() = QS_DISABLE;				qs_approved_ = 0;			}		} else { // Quick Start rejected			printf("Quick Start rejected\n");			rbp_mode_ = RBP_OFF;			qsh->flag() = QS_DISABLE;			qs_approved_ = 0;		}	} else if (acked_ == 1 && qs_approved_ == 1) {		//don't have to do anything here, RBP is doing exactly what we need	}	NewRenoTcpAgent::recv(pkt, hand);}voidQSNewRenoTcpAgent::timeout(int tno){	if (tno == TCP_TIMER_RTX) {		if (highest_ack_ == maxseq_) {			// Idle for a while => RBP next time.			//rbp_mode_ = RBP_POSSIBLE;			rbp_mode_ = RBP_OFF; //this is not an RBP implementation			return;		}		else {			rbp_mode_ = RBP_OFF; //turn off RBP			cwnd_ = initial_window();		}	}	NewRenoTcpAgent::timeout(tno);}voidQSNewRenoTcpAgent::send_much(int force, int reason, int maxburst){	if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {		// start paced mode		rbp_mode_ = RBP_GOING; 		rbp_segs_actually_paced_ = 0;		// Pace out scaled cwnd.		double rbwin_reno;		rbwin_reno = cwnd_ * rbp_scale_;		rbwin_reno = int(rbwin_reno + 0.5);   // round		// Always pace at least RBP_MIN_SEGMENTS		if (rbwin_reno <= RBP_MIN_SEGMENTS) {			rbwin_reno = RBP_MIN_SEGMENTS;		};		// Conservatively set the congestion window to min of		// congestion window and the smoothed rbwin_reno		RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));		cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_reno);		RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));		RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));		// RBP timer calculations must be based on the actual		// window which is the min of the receiver's		// advertised window and the congestion window.		// TcpAgent::window() does this job.		// What this means is we expect to send window() pkts		// in v_srtt_ time.		static double srtt_scale = 0.0;		if (srtt_scale == 0.0) {  // yuck yuck yuck!			srtt_scale = 1.0; // why are we doing fixed point?			int i;			for (i = T_SRTT_BITS; i > 0; i--) {				srtt_scale /= 2.0;			};		}		rbp_inter_pace_delay_ = (t_srtt_ * srtt_scale * tcp_tick_) / (window() * 1.0);		RBP_DEBUG_PRINTF(("window is %d\n", window()));		RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));		paced_send_one();	} else {		NewRenoTcpAgent::send_much(force,reason, maxburst);	};}voidQSNewRenoTcpAgent::paced_send_one(){	if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {		RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));		// send one packet		output(t_seqno_++, TCP_REASON_RBP);		rbp_segs_actually_paced_++;		// schedule next pkt		pace_timer_.resched(rbp_inter_pace_delay_);	};}intQSNewRenoTcpAgent::able_to_rbp_send_one(){	return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();}void QSNewRenoTcpAgent::output(int seqno, int reason){	int force_set_rtx_timer = 0;	Packet* p = allocpkt();	hdr_tcp *tcph = hdr_tcp::access(p);	hdr_ip *iph = hdr_ip::access(p);	hdr_qs *qsh = hdr_qs::access(p);	hdr_flags* hf = hdr_flags::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);	//iph->flowid() = session_id_;	if (seqno == 0) {		qsh->flag() = QS_REQUEST;		qsh->ttl() = Random::integer(256);		ttl_diff_ = (iph->ttl() - qsh->ttl()) % 256;                // PS: rate_request_ parameter is in KB/sec                qsh->rate() = hdr_qs::Bps_to_rate(rate_request_ * 1024);	}	else {		qsh->flag() = QS_DISABLE;	}	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_;			//printf("inside initial syn packet\n");		}		if (ecn_) {			hf->ecnecho() = 1;//			hf->cong_action() = 1;			hf->ect() = 0;		}	}	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);	//printf("wnd_ %f, cwnd_ %f, ssthresh_ %f\n", wnd_+0, cwnd_+0, ssthresh_+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();}class QSTcpSink;class QSTcpSink : public TcpSink {public:	QSTcpSink(Acker *);	virtual void ack(Packet * oPacket);	void recv(Packet* pkt, Handler*);};static class QSTcpSinkClass : public TclClass {public:	QSTcpSinkClass() : TclClass("Agent/TCPSink/QS") {}	TclObject* create(int, const char*const*) {		return (new QSTcpSink(new Acker));	}} class_sink_qs;QSTcpSink::QSTcpSink(Acker * acker) : TcpSink(acker) {}void QSTcpSink::recv(Packet* pkt, Handler*){	int numToDeliver;	int numBytes = hdr_cmn::access(pkt)->size();	// number of bytes in the packet just received	hdr_tcp *th = hdr_tcp::access(pkt);	/* W.N. Check if packet is from previous incarnation */	if (th->ts() < lastreset_) {		// Remove packet and do nothing		Packet::free(pkt);		return;	}	acker_->update_ts(th->seqno(),th->ts(),ts_echo_rfc1323_);	// update the timestamp to echo		numToDeliver = acker_->update(th->seqno(), numBytes);	// update the recv window; figure out how many in-order-bytes	// (if any) can be removed from the window and handed to the	// application	if (numToDeliver)		recvBytes(numToDeliver);	// send any packets to the application		  ack(pkt);	// ACK the packet	Packet::free(pkt);	// remove it from the system}void QSTcpSink::ack(Packet* opkt){	Packet* npkt = allocpkt();	// opkt is the "old" packet that was received	// npkt is the "new" packet being constructed (for the ACK)	double now = Scheduler::instance().clock();	hdr_tcp *otcp = hdr_tcp::access(opkt);	hdr_tcp *ntcp = hdr_tcp::access(npkt);	hdr_ip *oiph = hdr_ip::access(opkt);	hdr_qs *oqsh = hdr_qs::access(opkt);	hdr_qs *nqsh = hdr_qs::access(npkt);	if (otcp->seqno() == 0 && oqsh->flag() == QS_REQUEST) {		nqsh->flag() = QS_RESPONSE;		nqsh->ttl() = (oiph->ttl() - oqsh->ttl()) % 256;		nqsh->rate() = oqsh->rate();	}	else {		nqsh->flag() = QS_DISABLE;	}		// get the tcp headers	ntcp->seqno() = acker_->Seqno();	// get the cumulative sequence number to put in the ACK; this	// is just the left edge of the receive window - 1	ntcp->ts() = now;	// timestamp the packet	if (ts_echo_bugfix_)  /* TCP/IP Illustrated, Vol. 2, pg. 870 */		ntcp->ts_echo() = acker_->ts_to_echo();	else		ntcp->ts_echo() = otcp->ts();	// echo the original's time stamp	// hdr_ip* oip = hdr_ip::access(opkt);	// hdr_ip* nip = hdr_ip::access(npkt);	// get the ip headers	//nip->flowid() = oip->flowid();	// copy the flow id		hdr_flags* of = hdr_flags::access(opkt);	hdr_flags* nf = hdr_flags::access(npkt);	hdr_flags* sf;	if (save_ != NULL) {		sf = hdr_flags::access(save_);	} else {	        sf = 0;	}		// Look at delayed packet being acked. 	if ( (sf != 0 && sf->cong_action()) || of->cong_action() ) 		// Sender has responsed to congestion. 		acker_->update_ecn_unacked(0);	if ( (sf != 0 && sf->ect() && sf->ce())  || 			(of->ect() && of->ce()) )		// New report of congestion.  		acker_->update_ecn_unacked(1);	if ( (sf != 0 && sf->ect()) || of->ect() )		// Set EcnEcho bit.  		nf->ecnecho() = acker_->ecn_unacked();	if (!of->ect() && of->ecnecho() ||		(sf != 0 && !sf->ect() && sf->ecnecho()) ) 		 // This is the negotiation for ECN-capability.		 // We are not checking for of->cong_action() also. 		 // In this respect, this does not conform to the 		 // specifications in the internet draft 		nf->ecnecho() = 1;	acker_->append_ack(hdr_cmn::access(npkt),			   ntcp, otcp->seqno());	add_to_ack(npkt);	// the above function is used in TcpAsymSink		send(npkt, 0);	// send it}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -