diff options
Diffstat (limited to 'lib/server/TcpNice.h')
-rw-r--r-- | lib/server/TcpNice.h | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/lib/server/TcpNice.h b/lib/server/TcpNice.h new file mode 100644 index 00000000..e2027749 --- /dev/null +++ b/lib/server/TcpNice.h @@ -0,0 +1,174 @@ +// -------------------------------------------------------------------------- +// +// 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 <memory> + +#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<SocketStream> mapSocket; + TcpNice mTcpNice; + std::auto_ptr<Timer> 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<SocketStream> 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 |