// -------------------------------------------------------------------------- // // File // Name: TcpNice.h // Purpose: Calculator for adaptive TCP window sizing to support // low-priority background flows using the stochastic // algorithm, as described in // http://www.thlab.net/~lmassoul/p18-key.pdf // Created: 11/02/2012 // // -------------------------------------------------------------------------- #ifndef TCPNICE__H #define TCPNICE__H #include #include "SocketStream.h" #include "Timer.h" // -------------------------------------------------------------------------- // // Class // Name: TcpNice // Purpose: Calculator for adaptive TCP window sizing. // Created: 11/02/2012 // // -------------------------------------------------------------------------- class TcpNice { public: TcpNice(); int GetNextWindowSize(int bytesChange, box_time_t timeElapsed, int rttEstimateMicros); private: /** * The previous (last recommended) window size is one of the parameters * used to calculate the next window size. */ int mLastWindowSize; /** * Controls the speed of adaptation and the variance (random variation) * of the stable state in response to noise. The paper suggests using * 1.0 (100%). */ int mGammaPercent; /** * Controls the extent to which background flows are allowed to affect * foreground flows. Its detailed meaning is not explained in the paper, * but its units are bytes, and I think it controls how aggressive we * are at increasing window size, potentially at the expense of other * competing flows. */ int mAlphaStar; /** * Controls the speed of adaptation of the exponential weighted moving * average (EWMA) estimate of the bandwidth available to this flow. * The paper uses 10%. */ int mDeltaPercent; /** * The stochastic algorithm in the paper uses the rate estimate for the * last-but-one period (rbHat[n-2]) to calculate the next window size. * So we keep both the last (in rateEstimateMovingAverage[1]) and the * last-but-one (in rateEstimateMovingAverage[0]) values. */ int mRateEstimateMovingAverage[2]; }; // -------------------------------------------------------------------------- // // Class // Name: NiceSocketStream // Purpose: Wrapper around a SocketStream to limit sending rate to // avoid interference with higher-priority flows. // Created: 11/02/2012 // // -------------------------------------------------------------------------- class NiceSocketStream : public IOStream { private: std::auto_ptr mapSocket; TcpNice mTcpNice; std::auto_ptr mapTimer; int mBytesWrittenThisPeriod; box_time_t mPeriodStartTime; /** * The control interval T from the paper, in milliseconds. The available * bandwidth is estimated over this period, and the window size is * recalculated at the end of each period. It should be long enough for * TCP to adapt to a change in window size; perhaps 10-100 RTTs. One * second (1000) is probably a good first approximation in many cases. */ int mTimeIntervalMillis; /** * Because our data use is bursty, and tcp nice works on the assumption * that we've always got data to send, we should only enable nice mode * when we're doing a bulk upload, and disable it afterwards. */ bool mEnabled; void StartTimer() { mapTimer.reset(new Timer(mTimeIntervalMillis, "NiceSocketStream")); } void StopTimer() { mapTimer.reset(); } public: NiceSocketStream(std::auto_ptr apSocket); virtual ~NiceSocketStream() { // Be nice about closing the socket mapSocket->Shutdown(); mapSocket->Close(); } // This is the only magic virtual void Write(const void *pBuffer, int NBytes); // Everything else is delegated to the sink virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite) { return mapSocket->Read(pBuffer, NBytes, Timeout); } virtual pos_type BytesLeftToRead() { return mapSocket->BytesLeftToRead(); } virtual pos_type GetPosition() const { return mapSocket->GetPosition(); } virtual void Seek(IOStream::pos_type Offset, int SeekType) { mapSocket->Seek(Offset, SeekType); } virtual void Flush(int Timeout = IOStream::TimeOutInfinite) { mapSocket->Flush(Timeout); } virtual void Close() { mapSocket->Close(); } virtual bool StreamDataLeft() { return mapSocket->StreamDataLeft(); } virtual bool StreamClosed() { return mapSocket->StreamClosed(); } virtual void SetEnabled(bool enabled); private: NiceSocketStream(const NiceSocketStream &rToCopy) { /* do not call */ } }; #endif // TCPNICE__H