1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// --------------------------------------------------------------------------
//
// 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 SocketStream
{
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);
off_t GetBytesRead() const { return mapSocket->GetBytesRead(); }
off_t GetBytesWritten() const { return mapSocket->GetBytesWritten(); }
void ResetCounters() { mapSocket->ResetCounters(); }
private:
NiceSocketStream(const NiceSocketStream &rToCopy)
{ /* do not call */ }
};
#endif // TCPNICE__H
|