Compare commits
8 Commits
ndn-tools-22.12
...
0.7.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 785cfde06d | |||
| 507c60422f | |||
| a6e53e66e4 | |||
| 9724c94e33 | |||
| 3168ad7bd1 | |||
| 9648808618 | |||
| 1675c0aca0 | |||
| a23e941b13 |
@@ -68,6 +68,7 @@
|
||||
#include <ndn-cxx/util/scheduler.hpp>
|
||||
#include <ndn-cxx/util/signal.hpp>
|
||||
#include <ndn-cxx/util/time.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn {
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#include "bbr-consumer.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
BbrConsumer::BbrConsumer(Face &face, const Options &options)
|
||||
: Consumer(face, options),
|
||||
m_inFlight(0),
|
||||
fixedRate(options.fixedRate),
|
||||
delayStart(options.delayStart),
|
||||
timingStop(options.timingStop),
|
||||
delayGreedy(options.delayGreedy),
|
||||
greedyRate(options.greedyRate),
|
||||
dsz(options.dsz),
|
||||
m_minRtt(std::numeric_limits<double>::max()),
|
||||
m_maxBandwidth(0.0),
|
||||
m_PacingGain(2 / std::log(2)),
|
||||
m_CwndGain(2 / std::log(2)),
|
||||
m_mode(STARTUP),
|
||||
m_roundCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
void BbrConsumer::startGreedy()
|
||||
{
|
||||
this->fixedRate = this->greedyRate;
|
||||
}
|
||||
|
||||
void BbrConsumer::scheduleNextPacket()
|
||||
{
|
||||
if (m_stopFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->m_firstTime)
|
||||
{
|
||||
this->m_firstTime = false;
|
||||
if (this->timingStop > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->timingStop), [this]
|
||||
{ stop(); });
|
||||
}
|
||||
if (this->delayGreedy > 0 && this->greedyRate > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->delayGreedy), [this]
|
||||
{ startGreedy(); });
|
||||
}
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::milliseconds(this->delayStart), [this]
|
||||
{ sendPacket(); });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->fixedRate > 0)
|
||||
{
|
||||
auto waitTime = (this->dsz * 1000000000) / this->fixedRate + 1;
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::nanoseconds(waitTime), [this]
|
||||
{ sendPacket(); });
|
||||
return;
|
||||
}
|
||||
if (!m_nextInterestEvent)
|
||||
{
|
||||
auto bdp = m_minRtt * m_maxBandwidth;
|
||||
// std::cout << "BDP: " << bdp << ", inFlight: " << m_inFlight << ", minRtt: " << m_minRtt << ", maxBandwidth: " << m_maxBandwidth << std::endl;
|
||||
if (m_mode == DRAIN && m_inFlight < bdp)
|
||||
{
|
||||
doGainCycle();
|
||||
enterProbeBandwidthMode();
|
||||
|
||||
// Enter ProbeRTT mode every 10 seconds
|
||||
m_scheduler.schedule(time::seconds(10), [this]
|
||||
{ enterProbeRttMode(); });
|
||||
}
|
||||
|
||||
if (m_mode == PROBE_RTT)
|
||||
{
|
||||
if (m_inFlight >= 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::milliseconds(250), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_inFlight >= m_CwndGain * bdp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint32_t waitTime = m_packetSize / (m_PacingGain * m_maxBandwidth) * 1000000000;
|
||||
// std::cout << "waitTime: " << m_packetSize / (m_PacingGain * m_maxBandwidth) << std::endl;
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::nanoseconds(waitTime), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BbrConsumer::willSendInterest(uint32_t seq)
|
||||
{
|
||||
|
||||
m_inFlight++;
|
||||
Consumer::willSendInterest(seq);
|
||||
}
|
||||
|
||||
void BbrConsumer::onTimeout(uint32_t seq)
|
||||
{
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight--;
|
||||
}
|
||||
|
||||
Consumer::onTimeout(seq);
|
||||
}
|
||||
|
||||
void BbrConsumer::onData(const Data &data, uint32_t seq, const BbrPacketInfo &bbrInfo)
|
||||
{
|
||||
Consumer::onData(data, seq, bbrInfo);
|
||||
|
||||
if (!bbrInfo.isRetx)
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
// 1. Update minimum RTT
|
||||
auto rtt = m_rtt.duration_to_second_double(now - bbrInfo.sendTime);
|
||||
if (m_minRtt > rtt)
|
||||
{
|
||||
m_minRtt = rtt;
|
||||
}
|
||||
|
||||
// 2. Calculate the current delivery rate
|
||||
auto delivered_rate = (m_delivered - bbrInfo.deliveredAtSend) / m_rtt.duration_to_second_double((m_deliveredTime - bbrInfo.deliveredTime));
|
||||
// std::cout << "delivered_rate: " << delivered_rate << ", m_delivered: " << m_delivered << ", deliveredAtSend: " << bbrInfo.deliveredAtSend << ", rtt: " << m_rtt.duration_to_second_double(m_deliveredTime - bbrInfo.deliveredTime) << std::endl;
|
||||
if (delivered_rate > m_maxBandwidth)
|
||||
{
|
||||
m_maxBandwidth = delivered_rate;
|
||||
}
|
||||
else if (m_mode == STARTUP)
|
||||
{
|
||||
m_mode = DRAIN;
|
||||
m_PacingGain = std::log(2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight--;
|
||||
}
|
||||
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void BbrConsumer::doGainCycle()
|
||||
{
|
||||
m_roundCount++;
|
||||
auto gainStage = m_roundCount % 8;
|
||||
if (gainStage == 0)
|
||||
{
|
||||
m_PacingGain = 1.25;
|
||||
}
|
||||
else if (gainStage == 1)
|
||||
{
|
||||
m_PacingGain = 0.75;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacingGain = 1;
|
||||
}
|
||||
uint32_t waitTime = m_minRtt * 1000000000;
|
||||
m_doGainCycleEvent = m_scheduler.schedule(time::nanoseconds(waitTime), [this]
|
||||
{ doGainCycle(); });
|
||||
}
|
||||
|
||||
void BbrConsumer::enterProbeBandwidthMode()
|
||||
{
|
||||
m_mode = PROBE_BW;
|
||||
}
|
||||
|
||||
void BbrConsumer::enterProbeRttMode()
|
||||
{
|
||||
m_mode = PROBE_RTT;
|
||||
|
||||
auto finishRttProbeDuration = m_minRtt;
|
||||
if (finishRttProbeDuration > 0.2)
|
||||
{
|
||||
finishRttProbeDuration = 0.2;
|
||||
}
|
||||
|
||||
uint32_t waitTime = finishRttProbeDuration * 1000000000;
|
||||
|
||||
// Leave PROBE_RTT mode after max(rtt, 200ms)
|
||||
m_scheduler.schedule(time::nanoseconds(waitTime),
|
||||
[this]
|
||||
{
|
||||
enterProbeBandwidthMode();
|
||||
});
|
||||
|
||||
// Enter PROBE_RTT again after 10 seconds
|
||||
m_probeRTTEvent = m_scheduler.schedule(time::seconds(10), [this]
|
||||
{ enterProbeRttMode(); });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef BBR_CONSUMER_H
|
||||
#define BBR_CONSUMER_H
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class BbrConsumer : public Consumer
|
||||
{
|
||||
public:
|
||||
enum BbrMode
|
||||
{
|
||||
STARTUP,
|
||||
DRAIN,
|
||||
PROBE_BW,
|
||||
PROBE_RTT
|
||||
};
|
||||
|
||||
explicit BbrConsumer(Face &face, const Options &options);
|
||||
|
||||
virtual void onData(const Data &data, uint32_t seq, const BbrPacketInfo &bbrInfo);
|
||||
|
||||
virtual void onTimeout(uint32_t seq);
|
||||
|
||||
virtual void willSendInterest(uint32_t seq);
|
||||
|
||||
protected:
|
||||
virtual void scheduleNextPacket();
|
||||
|
||||
void startGreedy();
|
||||
|
||||
void enterProbeBandwidthMode();
|
||||
|
||||
void enterProbeRttMode();
|
||||
|
||||
void doGainCycle();
|
||||
|
||||
private:
|
||||
// window
|
||||
uint32_t m_inFlight;
|
||||
|
||||
int32_t fixedRate;
|
||||
uint64_t delayStart;
|
||||
int32_t timingStop;
|
||||
int32_t delayGreedy;
|
||||
int32_t greedyRate;
|
||||
uint64_t dsz;
|
||||
|
||||
// bbr
|
||||
double m_minRtt;
|
||||
double m_maxBandwidth;
|
||||
double m_PacingGain;
|
||||
double m_CwndGain;
|
||||
BbrMode m_mode;
|
||||
uint32_t m_roundCount;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BBR_CONSUMER_H
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "core/common.hpp"
|
||||
#include "core/version.hpp"
|
||||
#include "bbr-consumer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class Runner : noncopyable
|
||||
{
|
||||
public:
|
||||
explicit Runner(const Options &options) : m_consumer(m_face, options)
|
||||
{
|
||||
m_consumer.afterFinish.connect([this]
|
||||
{ this->cancel(); });
|
||||
}
|
||||
|
||||
int
|
||||
run()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_consumer.start();
|
||||
m_face.processEvents();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
// m_tracer.onError(e.what());
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
cancel()
|
||||
{
|
||||
m_consumer.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
Face m_face;
|
||||
BbrConsumer m_consumer;
|
||||
};
|
||||
|
||||
static void
|
||||
usage(const boost::program_options::options_description &options)
|
||||
{
|
||||
std::cout << "Usage: bbr-client [options] ndn:/name/prefix\n"
|
||||
"\n"
|
||||
"Ping a NDN name prefix using Interests with name ndn:/name/prefix/ping/number.\n"
|
||||
"The numbers in the Interests are randomly generated unless specified.\n"
|
||||
"\n"
|
||||
<< options;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Options options;
|
||||
// options.shouldAllowStaleData = false;
|
||||
// options.nPings = -1;
|
||||
// options.interval = time::milliseconds(getDefaultPingInterval());
|
||||
// options.timeout = time::milliseconds(getDefaultPingTimeoutThreshold());
|
||||
// options.startSeq = 0;
|
||||
// options.shouldGenerateRandomSeq = true;
|
||||
// options.shouldPrintTimestamp = false;
|
||||
|
||||
// std::string identifier;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
po::options_description visibleOptDesc("Options");
|
||||
visibleOptDesc.add_options()("help,h", "print this message and exit")("version,V", "display version and exit");
|
||||
visibleOptDesc.add_options()(
|
||||
"startSeq", po::value<uint32_t>(&options.startSeq)->default_value(0), "start sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"seqMax", po::value<int64_t>(&options.seqMax)->default_value(-1), "maximum sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"dsz", po::value<uint32_t>(&options.dsz)->default_value(8624), "data size");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayStart", po::value<uint32_t>(&options.delayStart)->default_value(0), "delay start time, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"fixedRate", po::value<int32_t>(&options.fixedRate)->default_value(-1), "fixed rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"timingStop", po::value<int32_t>(&options.timingStop)->default_value(-1), "timing stop, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayGreedy", po::value<int32_t>(&options.delayGreedy)->default_value(-1), "delay greedy, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"greedyRate", po::value<int32_t>(&options.greedyRate)->default_value(-1), "greedy rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"lifetime", po::value<time::milliseconds::rep>()->default_value(4000), "Interest lifetime, in milliseconds");
|
||||
|
||||
po::options_description hiddenOptDesc;
|
||||
hiddenOptDesc.add_options()("prefix", po::value<std::string>(), "content prefix to request");
|
||||
|
||||
po::options_description optDesc;
|
||||
optDesc.add(visibleOptDesc).add(hiddenOptDesc);
|
||||
|
||||
po::positional_options_description optPos;
|
||||
optPos.add("prefix", -1);
|
||||
|
||||
try
|
||||
{
|
||||
po::variables_map optVm;
|
||||
po::store(po::command_line_parser(argc, argv).options(optDesc).positional(optPos).run(), optVm);
|
||||
po::notify(optVm);
|
||||
|
||||
if (optVm.count("help") > 0)
|
||||
{
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
if (optVm.count("version") > 0)
|
||||
{
|
||||
std::cout << "qsccp-client " << tools::VERSION << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (optVm.count("prefix") > 0)
|
||||
{
|
||||
options.prefix = Name(optVm["prefix"].as<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: No prefix specified" << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
options.lifetime = time::milliseconds(optVm["lifetime"].as<time::milliseconds::rep>());
|
||||
}
|
||||
catch (const po::error &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
return Runner(options).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return ndn::cc::client::main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
Consumer::Consumer(Face &face, const Options &options)
|
||||
: m_options(options),
|
||||
m_nSent(0),
|
||||
m_nextSeq(options.startSeq),
|
||||
m_seqMax(options.seqMax),
|
||||
m_nOutstanding(0),
|
||||
m_face(face),
|
||||
m_scheduler(m_face.getIoService()),
|
||||
m_stopFlag(false),
|
||||
m_recvBytes(0),
|
||||
m_traceTimes(0),
|
||||
m_firstTime(true),
|
||||
m_delivered(0),
|
||||
m_deliveredTime(time::steady_clock::now()),
|
||||
m_packetSize(1067)
|
||||
|
||||
{
|
||||
if (m_options.seqMax < 0)
|
||||
{
|
||||
m_seqMax = std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(50),
|
||||
bind(&Consumer::checkRetxTimeout, this));
|
||||
}
|
||||
|
||||
void Consumer::traceRate()
|
||||
{
|
||||
if (!m_stopFlag)
|
||||
{
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
}
|
||||
|
||||
m_traceTimes++;
|
||||
// print rate
|
||||
std::cout << "cc:rate:<" << m_traceTimes << "," << m_recvBytes << ">" << m_nSent << std::endl;
|
||||
m_recvBytes = 0;
|
||||
m_nSent = 0;
|
||||
}
|
||||
|
||||
void Consumer::start()
|
||||
{
|
||||
m_stopFlag = false;
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void Consumer::stop()
|
||||
{
|
||||
m_nextInterestEvent.cancel();
|
||||
m_retxEvent.cancel();
|
||||
m_probeRTTEvent.cancel();
|
||||
m_doGainCycleEvent.cancel();
|
||||
m_stopFlag = true;
|
||||
}
|
||||
|
||||
void Consumer::checkRetxTimeout()
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
|
||||
auto rto = m_rtt.RetransmitTimeout();
|
||||
|
||||
std::cout << "RTO: " << m_rtt.duration_to_second_double(rto) << std::endl;
|
||||
|
||||
while (!m_seqTimeouts.empty())
|
||||
{
|
||||
SeqTimeoutsContainer::index<i_timestamp>::type::iterator entry =
|
||||
m_seqTimeouts.get<i_timestamp>().begin();
|
||||
if (entry->time + rto <= now) // timeout expired?
|
||||
{
|
||||
// std::cout << "RTOTIMEOUT: " << entry->seq << std::endl;
|
||||
uint32_t seqNo = entry->seq;
|
||||
if (m_seqRetxCounts.find(seqNo) != m_seqRetxCounts.end())
|
||||
{
|
||||
m_seqTimeouts.get<i_timestamp>().erase(entry);
|
||||
onTimeout(seqNo);
|
||||
}
|
||||
}
|
||||
else
|
||||
break; // nothing else to do. All later packets need not be retransmitted
|
||||
}
|
||||
|
||||
m_retxEvent = m_scheduler.schedule(time::milliseconds(50), bind(&Consumer::checkRetxTimeout, this));
|
||||
}
|
||||
|
||||
void Consumer::sendPacket()
|
||||
{
|
||||
uint32_t seq = std::numeric_limits<uint32_t>::max(); // invalid
|
||||
bool isRetx = false;
|
||||
while (m_retxSeqs.size())
|
||||
{
|
||||
seq = *m_retxSeqs.begin();
|
||||
m_retxSeqs.erase(m_retxSeqs.begin());
|
||||
isRetx = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (seq == std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_seqMax != std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_nextSeq >= m_seqMax)
|
||||
{
|
||||
return; // we are totally done
|
||||
}
|
||||
}
|
||||
seq = m_nextSeq++;
|
||||
}
|
||||
|
||||
Interest interest(makeInterestName(seq));
|
||||
interest.setCanBePrefix(false);
|
||||
interest.setMustBeFresh(true);
|
||||
interest.setServiceClass(5);
|
||||
interest.setInterestLifetime(m_options.lifetime);
|
||||
|
||||
auto now = time::steady_clock::now();
|
||||
BbrPacketInfo bbrPacketInfo{
|
||||
now,
|
||||
m_delivered,
|
||||
m_deliveredTime,
|
||||
isRetx
|
||||
};
|
||||
m_face.expressInterest(interest,
|
||||
bind(&Consumer::onData, this, _2, seq, bbrPacketInfo),
|
||||
bind(&Consumer::onNack, this, _2, seq, bbrPacketInfo),
|
||||
bind(&Consumer::onTimeout, this, seq));
|
||||
|
||||
willSendInterest(seq);
|
||||
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void Consumer::onTimeout(uint32_t seq)
|
||||
{
|
||||
std::cout << "onTimeout: " << seq << std::endl;
|
||||
afterTimeout(seq);
|
||||
|
||||
m_rtt.IncreaseMultiplier(); // Double the next RTO
|
||||
m_rtt.SentSeq(seq, 1); // make sure to disable RTT calculation for this sample
|
||||
m_retxSeqs.insert(seq);
|
||||
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onData(const Data &data, uint32_t seq, const BbrPacketInfo &bbrInfo)
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
std::cout << "cc:delay:<" << (now - bbrInfo.sendTime).count() << "," << seq << "," << (now - bbrInfo.sendTime).count() << ">" << std::endl;
|
||||
|
||||
// afterData(seq, now - sendTime);
|
||||
if (m_seqRetxCounts.find(seq) != m_seqRetxCounts.end())
|
||||
{
|
||||
auto dataSize = data.wireEncode().size();
|
||||
m_packetSize = 0.8 * m_packetSize + 0.2 * dataSize;
|
||||
m_recvBytes += dataSize;
|
||||
m_delivered += dataSize;
|
||||
m_deliveredTime = now;
|
||||
m_seqRetxCounts.erase(seq);
|
||||
}
|
||||
|
||||
m_seqFullDelay.erase(seq);
|
||||
m_seqLastDelay.erase(seq);
|
||||
|
||||
m_seqTimeouts.erase(seq);
|
||||
m_retxSeqs.erase(seq);
|
||||
|
||||
m_rtt.AckSeq(seq);
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onNack(const lp::Nack &nack, uint32_t seq, const BbrPacketInfo &bbrInfo)
|
||||
{
|
||||
std::cout << "onNack: " << nack.getReason() << std::endl;
|
||||
afterNack(seq, time::steady_clock::now() - bbrInfo.sendTime, nack.getHeader());
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
Name Consumer::makeInterestName(uint32_t seq)
|
||||
{
|
||||
return Name(m_options.prefix).appendNumber(seq);
|
||||
}
|
||||
|
||||
void Consumer::willSendInterest(uint32_t seq)
|
||||
{
|
||||
++m_nSent;
|
||||
m_seqTimeouts.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
m_seqFullDelay.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
|
||||
m_seqLastDelay.erase(seq);
|
||||
m_seqLastDelay.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
|
||||
m_seqRetxCounts[seq]++;
|
||||
|
||||
m_rtt.SentSeq(seq, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#ifndef NDN_CONSUMER_H
|
||||
#define NDN_CONSUMER_H
|
||||
#include "core/common.hpp"
|
||||
#include "ndn-rtt-mean-deviation.hpp"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/tag.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
typedef time::duration<double, time::milliseconds::period> Rtt;
|
||||
|
||||
enum CcAlgorithm
|
||||
{
|
||||
AIMD,
|
||||
BIC,
|
||||
CUBIC
|
||||
};
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
Name prefix;
|
||||
uint32_t startSeq; // Initial sequence number
|
||||
time::milliseconds lifetime; // Lifetime of Interest
|
||||
int64_t seqMax; // Max sequence number
|
||||
uint32_t tos; // Type of Service
|
||||
uint32_t dsz; // Data size
|
||||
int32_t reqNum; // Request Data Num(-1 indicator infinity)
|
||||
uint32_t initialSendRate; // Initial Send Rate
|
||||
int32_t fixedRate; // Fixed Send Rate
|
||||
int32_t timingStop; // Timing Stop(-1 indicator infinity)
|
||||
int32_t delayGreedy; // Start greedy after the specified time (ms)
|
||||
int32_t greedyRate; // Greedy Send Rate(-1 indicator no greedy)
|
||||
uint32_t delayStart; // Delay Start(ms)
|
||||
};
|
||||
|
||||
struct BbrPacketInfo {
|
||||
// Interest packet send time
|
||||
time::steady_clock::TimePoint sendTime;
|
||||
// Total data bytes received while send this Interest
|
||||
uint64_t deliveredAtSend;
|
||||
// Last acked data received time while send this Interest
|
||||
time::steady_clock::TimePoint deliveredTime;
|
||||
bool isRetx;
|
||||
};
|
||||
|
||||
typedef struct BbrPacketInfo BbrPacketInfo;
|
||||
|
||||
class Consumer : noncopyable
|
||||
{
|
||||
public:
|
||||
Consumer(Face &face, const Options &options);
|
||||
|
||||
virtual ~Consumer() {}
|
||||
|
||||
signal::Signal<Consumer, uint32_t, Rtt> afterData;
|
||||
|
||||
signal::Signal<Consumer, uint32_t, Rtt, lp::NackHeader> afterNack;
|
||||
|
||||
signal::Signal<Consumer, uint32_t> afterTimeout;
|
||||
|
||||
signal::Signal<Consumer> afterFinish;
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
public:
|
||||
Name makeInterestName(uint32_t seq);
|
||||
|
||||
virtual void sendPacket();
|
||||
|
||||
virtual void onTimeout(uint32_t seq);
|
||||
|
||||
virtual void onData(const Data &data, uint32_t seq, const BbrPacketInfo &bbrInfo);
|
||||
|
||||
virtual void onNack(const lp::Nack &nack, uint32_t seq, const BbrPacketInfo &bbrInfo);
|
||||
|
||||
virtual void willSendInterest(uint32_t seq);
|
||||
|
||||
protected:
|
||||
virtual void scheduleNextPacket() = 0;
|
||||
|
||||
/**
|
||||
* \brief Checks if the packet need to be retransmitted becuase of retransmission timer expiration
|
||||
*/
|
||||
void
|
||||
checkRetxTimeout();
|
||||
|
||||
private:
|
||||
void traceRate();
|
||||
|
||||
protected:
|
||||
const Options &m_options;
|
||||
int m_nSent;
|
||||
uint32_t m_nextSeq;
|
||||
int64_t m_seqMax;
|
||||
int m_nOutstanding;
|
||||
Face &m_face;
|
||||
Scheduler m_scheduler;
|
||||
int m_stopFlag;
|
||||
|
||||
uint64_t m_recvBytes;
|
||||
uint64_t m_traceTimes;
|
||||
time::steady_clock::TimePoint m_startTime;
|
||||
scheduler::EventId m_nextInterestEvent;
|
||||
scheduler::EventId m_probeRTTEvent;
|
||||
scheduler::EventId m_doGainCycleEvent;
|
||||
bool m_firstTime = true;
|
||||
|
||||
// bbr
|
||||
uint64_t m_delivered;
|
||||
time::steady_clock::TimePoint m_deliveredTime;
|
||||
uint32_t m_packetSize = 8624;
|
||||
|
||||
/// @cond include_hidden
|
||||
/**
|
||||
* \struct This struct contains sequence numbers of packets to be retransmitted
|
||||
*/
|
||||
struct RetxSeqsContainer : public std::set<uint32_t>
|
||||
{
|
||||
};
|
||||
|
||||
RetxSeqsContainer m_retxSeqs; ///< \brief ordered set of sequence numbers to be retransmitted
|
||||
|
||||
RttMeanDeviation m_rtt;
|
||||
scheduler::EventId m_retxEvent;
|
||||
// RttEstimator m_rtt;
|
||||
/**
|
||||
* \struct This struct contains a pair of packet sequence number and its timeout
|
||||
*/
|
||||
struct SeqTimeout
|
||||
{
|
||||
SeqTimeout(uint32_t _seq, time::steady_clock::TimePoint _time)
|
||||
: seq(_seq), time(_time)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t seq;
|
||||
time::steady_clock::TimePoint time;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond include_hidden
|
||||
class i_seq
|
||||
{
|
||||
};
|
||||
class i_timestamp
|
||||
{
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond include_hidden
|
||||
/**
|
||||
* \struct This struct contains a multi-index for the set of SeqTimeout structs
|
||||
*/
|
||||
struct SeqTimeoutsContainer
|
||||
: public boost::multi_index::
|
||||
multi_index_container<SeqTimeout,
|
||||
boost::multi_index::
|
||||
indexed_by<boost::multi_index::
|
||||
ordered_unique<boost::multi_index::tag<i_seq>,
|
||||
boost::multi_index::
|
||||
member<SeqTimeout, uint32_t,
|
||||
&SeqTimeout::seq>>,
|
||||
boost::multi_index::
|
||||
ordered_non_unique<boost::multi_index::
|
||||
tag<i_timestamp>,
|
||||
boost::multi_index::
|
||||
member<SeqTimeout, time::steady_clock::TimePoint,
|
||||
&SeqTimeout::time>>>>
|
||||
{
|
||||
};
|
||||
|
||||
SeqTimeoutsContainer m_seqTimeouts; ///< \brief multi-index for the set of SeqTimeout structs
|
||||
|
||||
SeqTimeoutsContainer m_seqLastDelay;
|
||||
SeqTimeoutsContainer m_seqFullDelay;
|
||||
std::map<uint32_t, uint32_t> m_seqRetxCounts;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* RTT_ESTIMATOR_H */
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "ndn-rtt-estimator.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
void
|
||||
RttEstimator::SetMinRto(time::steady_clock::duration minRto)
|
||||
{
|
||||
m_minRto = minRto;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetMinRto(void) const
|
||||
{
|
||||
return m_minRto;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SetMaxRto(time::steady_clock::duration maxRto)
|
||||
{
|
||||
m_maxRto = maxRto;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetMaxRto(void) const
|
||||
{
|
||||
return m_maxRto;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SetCurrentEstimate(time::steady_clock::duration estimate)
|
||||
{
|
||||
m_currentEstimatedRtt = estimate;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetCurrentEstimate(void) const
|
||||
{
|
||||
return m_currentEstimatedRtt;
|
||||
}
|
||||
|
||||
// RttHistory methods
|
||||
RttHistory::RttHistory(uint32_t s, uint32_t c, time::steady_clock::TimePoint t)
|
||||
: seq(s), count(c), time(t), retx(false)
|
||||
{
|
||||
}
|
||||
|
||||
RttHistory::RttHistory(const RttHistory &h)
|
||||
: seq(h.seq), count(h.count), time(h.time), retx(h.retx)
|
||||
{
|
||||
}
|
||||
|
||||
// Base class methods
|
||||
RttEstimator::RttEstimator()
|
||||
: m_next(1),
|
||||
m_maxMultiplier(64),
|
||||
m_initialEstimatedRtt(second_double_to_duration(1)), // 1 seconds
|
||||
m_minRto(second_double_to_duration(0.2)), // 0.2 seconds
|
||||
m_maxRto(second_double_to_duration(200)), // 200 seconds
|
||||
m_nSamples(0),
|
||||
m_multiplier(1),
|
||||
m_history()
|
||||
{
|
||||
m_currentEstimatedRtt = m_initialEstimatedRtt;
|
||||
}
|
||||
|
||||
RttEstimator::RttEstimator(const RttEstimator &c)
|
||||
: m_next(c.m_next), m_maxMultiplier(c.m_maxMultiplier), m_initialEstimatedRtt(c.m_initialEstimatedRtt), m_currentEstimatedRtt(c.m_currentEstimatedRtt), m_minRto(c.m_minRto), m_maxRto(c.m_maxRto), m_nSamples(c.m_nSamples), m_multiplier(c.m_multiplier), m_history(c.m_history)
|
||||
{
|
||||
}
|
||||
|
||||
RttEstimator::~RttEstimator()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SentSeq(uint32_t seq, uint32_t size)
|
||||
{
|
||||
// Note that a particular sequence has been sent
|
||||
if (seq == m_next)
|
||||
{ // This is the next expected one, just log at end
|
||||
m_history.push_back(RttHistory(seq, size, time::steady_clock::now()));
|
||||
m_next = seq + uint32_t(size); // Update next expected
|
||||
}
|
||||
else
|
||||
{ // This is a retransmit, find in list and mark as re-tx
|
||||
for (RttHistory_t::iterator i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if ((seq >= i->seq) && (seq < (i->seq + uint32_t(i->count))))
|
||||
{ // Found it
|
||||
i->retx = true;
|
||||
// One final test..be sure this re-tx does not extend "next"
|
||||
if ((seq + uint32_t(size)) > m_next)
|
||||
{
|
||||
m_next = seq + uint32_t(size);
|
||||
i->count = ((seq + uint32_t(size)) - i->seq); // And update count in hist
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::AckSeq(uint32_t ackSeq)
|
||||
{
|
||||
// An ack has been received, calculate rtt and log this measurement
|
||||
// Note we use a linear search (O(n)) for this since for the common
|
||||
// case the ack'ed packet will be at the head of the list
|
||||
time::steady_clock::duration m = time::steady_clock::duration::zero();
|
||||
if (m_history.size() == 0)
|
||||
{
|
||||
return (m); // No pending history, just exit
|
||||
}
|
||||
RttHistory &h = m_history.front();
|
||||
if (!h.retx && ackSeq >= (h.seq + uint32_t(h.count)))
|
||||
{ // Ok to use this sample
|
||||
m = time::steady_clock::now() - h.time; // Elapsed time::nanoseconds
|
||||
Measurement(m); // Log the measurement
|
||||
ResetMultiplier(); // Reset multiplier on valid measurement
|
||||
}
|
||||
// Now delete all ack history with seq <= ack
|
||||
while (m_history.size() > 0)
|
||||
{
|
||||
RttHistory &h = m_history.front();
|
||||
if ((h.seq + uint32_t(h.count)) > ackSeq)
|
||||
break; // Done removing
|
||||
m_history.pop_front(); // Remove
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::ClearSent()
|
||||
{
|
||||
// Clear all history entries
|
||||
m_next = 1;
|
||||
m_history.clear();
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::IncreaseMultiplier()
|
||||
{
|
||||
m_multiplier = (m_multiplier * 2 < m_maxMultiplier) ? m_multiplier * 2 : m_maxMultiplier;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::ResetMultiplier()
|
||||
{
|
||||
m_multiplier = 1;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::Reset()
|
||||
{
|
||||
// Reset to initial state
|
||||
m_next = 1;
|
||||
m_currentEstimatedRtt = m_initialEstimatedRtt;
|
||||
m_history.clear(); // Remove all info from the history
|
||||
m_nSamples = 0;
|
||||
ResetMultiplier();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#ifndef NDN_RTT_ESTIMATOR_H
|
||||
#define NDN_RTT_ESTIMATOR_H
|
||||
#include "core/common.hpp"
|
||||
#include <deque>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
/**
|
||||
* \ingroup ndn-apps
|
||||
*
|
||||
* \brief Helper class to store RTT measurements
|
||||
*/
|
||||
class RttHistory
|
||||
{
|
||||
public:
|
||||
RttHistory(uint32_t seq, uint32_t size, time::steady_clock::TimePoint time);
|
||||
RttHistory(const RttHistory &h); // Copy constructor
|
||||
public:
|
||||
uint32_t seq; // First sequence number in packet sent
|
||||
uint32_t count; // Number of bytes sent
|
||||
time::steady_clock::TimePoint time; // Time this one was sent
|
||||
bool retx; // True if this has been retransmitted
|
||||
};
|
||||
|
||||
typedef std::deque<RttHistory> RttHistory_t;
|
||||
|
||||
/**
|
||||
* \ingroup tcp
|
||||
*
|
||||
* \brief Base class for all RTT Estimators
|
||||
*/
|
||||
class RttEstimator
|
||||
{
|
||||
public:
|
||||
RttEstimator();
|
||||
RttEstimator(const RttEstimator &);
|
||||
|
||||
virtual ~RttEstimator();
|
||||
|
||||
/**
|
||||
* \brief Note that a particular sequence has been sent
|
||||
* \param seq the packet sequence number.
|
||||
* \param size the packet size.
|
||||
*/
|
||||
virtual void
|
||||
SentSeq(uint32_t seq, uint32_t size);
|
||||
|
||||
/**
|
||||
* \brief Note that a particular ack sequence has been received
|
||||
* \param ackSeq the ack sequence number.
|
||||
* \return The measured RTT for this ack.
|
||||
*/
|
||||
virtual time::steady_clock::duration
|
||||
AckSeq(uint32_t ackSeq);
|
||||
|
||||
/**
|
||||
* \brief Clear all history entries
|
||||
*/
|
||||
virtual void
|
||||
ClearSent();
|
||||
|
||||
/**
|
||||
* \brief Add a new measurement to the estimator. Pure virtual function.
|
||||
* \param t the new RTT measure.
|
||||
*/
|
||||
virtual void
|
||||
Measurement(time::steady_clock::duration t) = 0;
|
||||
|
||||
/**
|
||||
* \brief Returns the estimated RTO. Pure virtual function.
|
||||
* \return the estimated RTO.
|
||||
*/
|
||||
virtual time::steady_clock::duration
|
||||
RetransmitTimeout() = 0;
|
||||
|
||||
/**
|
||||
* \brief Increase the estimation multiplier up to MaxMultiplier.
|
||||
*/
|
||||
virtual void
|
||||
IncreaseMultiplier();
|
||||
|
||||
/**
|
||||
* \brief Resets the estimation multiplier to 1.
|
||||
*/
|
||||
virtual void
|
||||
ResetMultiplier();
|
||||
|
||||
/**
|
||||
* \brief Resets the estimation to its initial state.
|
||||
*/
|
||||
virtual void
|
||||
Reset();
|
||||
|
||||
/**
|
||||
* \brief Sets the Minimum RTO.
|
||||
* \param minRto The minimum RTO returned by the estimator.
|
||||
*/
|
||||
void
|
||||
SetMinRto(time::steady_clock::duration minRto);
|
||||
|
||||
/**
|
||||
* \brief Get the Minimum RTO.
|
||||
* \return The minimum RTO returned by the estimator.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetMinRto(void) const;
|
||||
|
||||
/**
|
||||
* \brief Sets the Maximum RTO.
|
||||
* \param minRto The maximum RTO returned by the estimator.
|
||||
*/
|
||||
void
|
||||
SetMaxRto(time::steady_clock::duration maxRto);
|
||||
|
||||
/**
|
||||
* \brief Get the Maximum RTO.
|
||||
* \return The maximum RTO returned by the estimator.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetMaxRto(void) const;
|
||||
|
||||
/**
|
||||
* \brief Sets the current RTT estimate (forcefully).
|
||||
* \param estimate The current RTT estimate.
|
||||
*/
|
||||
void
|
||||
SetCurrentEstimate(time::steady_clock::duration estimate);
|
||||
|
||||
/**
|
||||
* \brief gets the current RTT estimate.
|
||||
* \return The current RTT estimate.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetCurrentEstimate(void) const;
|
||||
|
||||
double
|
||||
duration_to_second_double(time::steady_clock::duration d) {
|
||||
return d.count() / 1000000000.0;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
second_double_to_duration(double d) {
|
||||
return time::steady_clock::duration(static_cast<long long>(d * 1000000000.0));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_next; // Next expected sequence to be sent
|
||||
uint16_t m_maxMultiplier;
|
||||
time::steady_clock::duration m_initialEstimatedRtt;
|
||||
|
||||
protected:
|
||||
time::steady_clock::duration m_currentEstimatedRtt; // Current estimate
|
||||
time::steady_clock::duration m_minRto; // minimum value of the timeout
|
||||
time::steady_clock::duration m_maxRto; // maximum value of the timeout
|
||||
uint32_t m_nSamples; // Number of samples
|
||||
uint16_t m_multiplier; // RTO Multiplier
|
||||
RttHistory_t m_history; // List of sent packet
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* RTT_ESTIMATOR_H */
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "ndn-rtt-mean-deviation.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
RttMeanDeviation::RttMeanDeviation()
|
||||
: m_gain(0.125),
|
||||
m_gain2(0.25),
|
||||
m_variance(0)
|
||||
{
|
||||
}
|
||||
|
||||
RttMeanDeviation::RttMeanDeviation(const RttMeanDeviation &c)
|
||||
: RttEstimator(c), m_gain(c.m_gain), m_gain2(c.m_gain2), m_variance(c.m_variance)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Measurement(time::steady_clock::duration m)
|
||||
{
|
||||
if (m_nSamples)
|
||||
{ // Not first
|
||||
time::steady_clock::duration err(m - m_currentEstimatedRtt);
|
||||
double gErr = duration_to_second_double(err) * m_gain;
|
||||
|
||||
m_currentEstimatedRtt += second_double_to_duration(gErr);
|
||||
|
||||
auto abs_err = err > time::steady_clock::duration::zero() ? err : -err;
|
||||
|
||||
time::steady_clock::duration difference = abs_err - m_variance;
|
||||
m_variance += second_double_to_duration(m_gain2 * duration_to_second_double(difference));
|
||||
}
|
||||
else
|
||||
{ // First sample
|
||||
m_currentEstimatedRtt = m; // Set estimate to current
|
||||
// variance = sample / 2; // And variance to current / 2
|
||||
// m_variance = m; // try this why????
|
||||
m_variance = second_double_to_duration(duration_to_second_double(m) / 2);
|
||||
}
|
||||
m_nSamples++;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttMeanDeviation::RetransmitTimeout()
|
||||
{
|
||||
// std::cout << "RTO -> m_currentEstimatedRtt:" << duration_to_second_double(m_currentEstimatedRtt) << ", m_variance:" << duration_to_second_double(m_variance) << std::endl;
|
||||
double retval = std::min(
|
||||
duration_to_second_double(m_maxRto),
|
||||
std::max(
|
||||
m_multiplier * duration_to_second_double(m_minRto),
|
||||
m_multiplier * (duration_to_second_double(m_currentEstimatedRtt) + 4 * duration_to_second_double(m_variance))));
|
||||
return second_double_to_duration(retval);
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Reset()
|
||||
{
|
||||
// Reset to initial state
|
||||
m_variance = time::steady_clock::duration::zero();
|
||||
RttEstimator::Reset();
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Gain(double g)
|
||||
{
|
||||
m_gain = g;
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::SentSeq(uint32_t seq, uint32_t size)
|
||||
{
|
||||
RttHistory_t::iterator i;
|
||||
for (i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if (seq == i->seq)
|
||||
{ // Found it
|
||||
i->retx = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that a particular sequence has been sent
|
||||
if (i == m_history.end())
|
||||
m_history.push_back(RttHistory(seq, size, time::steady_clock::now()));
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttMeanDeviation::AckSeq(uint32_t ackSeq)
|
||||
{
|
||||
// An ack has been received, calculate rtt and log this measurement
|
||||
// Note we use a linear search (O(n)) for this since for the common
|
||||
// case the ack'ed packet will be at the head of the list
|
||||
auto m = time::steady_clock::duration::zero();
|
||||
if (m_history.size() == 0)
|
||||
return (m); // No pending history, just exit
|
||||
|
||||
for (RttHistory_t::iterator i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if (ackSeq == i->seq)
|
||||
{ // Found it
|
||||
if (!i->retx)
|
||||
{
|
||||
m = time::steady_clock::now() - i->time; // Elapsed time
|
||||
Measurement(m); // Log the measurement
|
||||
ResetMultiplier(); // Reset multiplier on valid measurement
|
||||
}
|
||||
m_history.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef NDN_RTT_MEAN_DEVIATION_H
|
||||
#define NDN_RTT_MEAN_DEVIATION_H
|
||||
|
||||
#include "ndn-rtt-estimator.hpp"
|
||||
|
||||
#endif /* NDN_RTT_MEAN_DEVIATION_H */
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
/**
|
||||
* \ingroup ndn-apps
|
||||
*
|
||||
* \brief The modified version of "Mean--Deviation" RTT estimator, as discussed by Van Jacobson that
|
||||
*better suits NDN communication model
|
||||
*
|
||||
* This class implements the "Mean--Deviation" RTT estimator, as discussed
|
||||
* by Van Jacobson and Michael J. Karels, in
|
||||
* "Congestion Avoidance and Control", SIGCOMM 88, Appendix A
|
||||
*
|
||||
*/
|
||||
class RttMeanDeviation : public RttEstimator
|
||||
{
|
||||
public:
|
||||
RttMeanDeviation();
|
||||
|
||||
RttMeanDeviation(const RttMeanDeviation &);
|
||||
|
||||
void
|
||||
SentSeq(uint32_t seq, uint32_t size);
|
||||
|
||||
time::steady_clock::duration
|
||||
AckSeq(uint32_t ackSeq);
|
||||
|
||||
void
|
||||
Measurement(time::steady_clock::duration measure);
|
||||
|
||||
time::steady_clock::duration
|
||||
RetransmitTimeout();
|
||||
|
||||
void
|
||||
Reset();
|
||||
|
||||
void
|
||||
Gain(double g);
|
||||
|
||||
private:
|
||||
double m_gain; // Filter gain
|
||||
double m_gain2; // Filter gain
|
||||
time::steady_clock::duration m_variance; // Current variance
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "core/common.hpp"
|
||||
#include "core/version.hpp"
|
||||
#include "qsccp-consumer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class Runner : noncopyable
|
||||
{
|
||||
public:
|
||||
explicit Runner(const Options &options) : m_consumer(m_face, options)
|
||||
{
|
||||
m_consumer.afterFinish.connect([this]
|
||||
{ this->cancel(); });
|
||||
}
|
||||
|
||||
int
|
||||
run()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_consumer.start();
|
||||
m_face.processEvents();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
// m_tracer.onError(e.what());
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
cancel()
|
||||
{
|
||||
m_consumer.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
Face m_face;
|
||||
QsccpConsumer m_consumer;
|
||||
};
|
||||
|
||||
static void
|
||||
usage(const boost::program_options::options_description &options)
|
||||
{
|
||||
std::cout << "Usage: ndnping [options] ndn:/name/prefix\n"
|
||||
"\n"
|
||||
"Ping a NDN name prefix using Interests with name ndn:/name/prefix/ping/number.\n"
|
||||
"The numbers in the Interests are randomly generated unless specified.\n"
|
||||
"\n"
|
||||
<< options;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Options options;
|
||||
// options.shouldAllowStaleData = false;
|
||||
// options.nPings = -1;
|
||||
// options.interval = time::milliseconds(getDefaultPingInterval());
|
||||
// options.timeout = time::milliseconds(getDefaultPingTimeoutThreshold());
|
||||
// options.startSeq = 0;
|
||||
// options.shouldGenerateRandomSeq = true;
|
||||
// options.shouldPrintTimestamp = false;
|
||||
|
||||
// std::string identifier;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
po::options_description visibleOptDesc("Options");
|
||||
visibleOptDesc.add_options()("help,h", "print this message and exit")("version,V", "display version and exit");
|
||||
visibleOptDesc.add_options()(
|
||||
"startSeq", po::value<uint64_t>(&options.startSeq)->default_value(0), "start sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"seqMax", po::value<int64_t>(&options.seqMax)->default_value(-1), "maximum sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"tos", po::value<uint32_t>(&options.tos)->default_value(5), "set the TOS field");
|
||||
visibleOptDesc.add_options()(
|
||||
"dsz", po::value<uint32_t>(&options.dsz)->default_value(8624), "data size");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayStart", po::value<uint32_t>(&options.delayStart)->default_value(0), "delay start time, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"initialSendRate", po::value<uint32_t>(&options.initialSendRate)->default_value(0), "initial send rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"fixedRate", po::value<int32_t>(&options.fixedRate)->default_value(-1), "fixed rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"timingStop", po::value<int32_t>(&options.timingStop)->default_value(-1), "timing stop, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayGreedy", po::value<int32_t>(&options.delayGreedy)->default_value(-1), "delay greedy, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"greedyRate", po::value<int32_t>(&options.greedyRate)->default_value(-1), "greedy rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"lifetime", po::value<time::milliseconds::rep>()->default_value(4000), "Interest lifetime, in milliseconds");
|
||||
|
||||
po::options_description hiddenOptDesc;
|
||||
hiddenOptDesc.add_options()("prefix", po::value<std::string>(), "content prefix to request");
|
||||
|
||||
po::options_description optDesc;
|
||||
optDesc.add(visibleOptDesc).add(hiddenOptDesc);
|
||||
|
||||
po::positional_options_description optPos;
|
||||
optPos.add("prefix", -1);
|
||||
|
||||
try
|
||||
{
|
||||
po::variables_map optVm;
|
||||
po::store(po::command_line_parser(argc, argv).options(optDesc).positional(optPos).run(), optVm);
|
||||
po::notify(optVm);
|
||||
|
||||
if (optVm.count("help") > 0)
|
||||
{
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
if (optVm.count("version") > 0)
|
||||
{
|
||||
std::cout << "qsccp-client " << tools::VERSION << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (optVm.count("prefix") > 0)
|
||||
{
|
||||
options.prefix = Name(optVm["prefix"].as<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: No prefix specified" << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
options.lifetime = time::milliseconds(optVm["lifetime"].as<time::milliseconds::rep>());
|
||||
}
|
||||
catch (const po::error &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
std::cout << "PING " << options.prefix << std::endl;
|
||||
return Runner(options).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return ndn::cc::client::main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
Consumer::Consumer(Face &face, const Options &options)
|
||||
: m_options(options),
|
||||
m_nSent(0),
|
||||
m_nextSeq(options.startSeq),
|
||||
m_seqMax(options.seqMax),
|
||||
m_nOutstanding(0),
|
||||
m_face(face),
|
||||
m_scheduler(m_face.getIoService()),
|
||||
m_stopFlag(false),
|
||||
m_recvBytes(0),
|
||||
m_traceTimes(0)
|
||||
{
|
||||
if (m_options.seqMax < 0)
|
||||
{
|
||||
m_seqMax = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
}
|
||||
|
||||
void Consumer::traceRate()
|
||||
{
|
||||
if (!m_stopFlag)
|
||||
{
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
}
|
||||
|
||||
m_traceTimes++;
|
||||
// print rate
|
||||
std::cout << "cc:rate:<" << m_traceTimes << "," << m_recvBytes << ">" << m_nSent << std::endl;
|
||||
m_recvBytes = 0;
|
||||
m_nSent = 0;
|
||||
}
|
||||
|
||||
void Consumer::start()
|
||||
{
|
||||
m_stopFlag = false;
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void Consumer::stop()
|
||||
{
|
||||
m_nextInterestEvent.cancel();
|
||||
m_stopFlag = true;
|
||||
}
|
||||
|
||||
void Consumer::sendPacket()
|
||||
{
|
||||
uint32_t seq = std::numeric_limits<uint32_t>::max(); // invalid
|
||||
while (m_retxSeqs.size())
|
||||
{
|
||||
seq = *m_retxSeqs.begin();
|
||||
m_retxSeqs.erase(m_retxSeqs.begin());
|
||||
break;
|
||||
}
|
||||
|
||||
if (seq == std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_seqMax != std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_nextSeq >= m_seqMax)
|
||||
{
|
||||
return; // we are totally done
|
||||
}
|
||||
}
|
||||
seq = m_nextSeq++;
|
||||
}
|
||||
|
||||
Interest interest(makeInterestName(seq));
|
||||
interest.setCanBePrefix(false);
|
||||
interest.setMustBeFresh(true);
|
||||
interest.setInterestLifetime(m_options.lifetime);
|
||||
|
||||
auto now = time::steady_clock::now();
|
||||
m_face.expressInterest(interest,
|
||||
bind(&Consumer::onData, this, _2, seq, now),
|
||||
bind(&Consumer::onNack, this, _2, seq, now),
|
||||
bind(&Consumer::onTimeout, this, seq));
|
||||
|
||||
++m_nSent;
|
||||
++m_nOutstanding;
|
||||
|
||||
scheduleNextPacket();
|
||||
|
||||
// if (m_nSent < m_seqMax)
|
||||
// {
|
||||
// m_nextInterestEvent = m_scheduler.schedule(m_options.interval, [this]
|
||||
// { scheduleNextInterest(); });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// finish();
|
||||
// }
|
||||
}
|
||||
|
||||
void Consumer::onTimeout(uint64_t seq)
|
||||
{
|
||||
m_retxSeqs.insert(seq);
|
||||
afterTimeout(seq);
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onData(const Data &data, uint64_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
std::cout << "cc:delay:<" << (now - m_startTime).count() << "," << seq << "," << (now - sendTime).count() << ">" << std::endl;
|
||||
|
||||
afterData(seq, now - sendTime);
|
||||
m_recvBytes += data.wireEncode().size();
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onNack(const lp::Nack &nack, uint64_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
std::cout << "onNack: " << nack.getReason() << std::endl;
|
||||
afterNack(seq, time::steady_clock::now() - sendTime, nack.getHeader());
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
Name Consumer::makeInterestName(uint64_t seq)
|
||||
{
|
||||
return Name(m_options.prefix).appendNumber(seq);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "core/common.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
typedef time::duration<double, time::milliseconds::period> Rtt;
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
Name prefix;
|
||||
uint64_t startSeq; // Initial sequence number
|
||||
time::milliseconds lifetime; // Lifetime of Interest
|
||||
int64_t seqMax; // Max sequence number
|
||||
uint32_t tos; // Type of Service
|
||||
uint32_t dsz; // Data size
|
||||
int32_t reqNum; // Request Data Num(-1 indicator infinity)
|
||||
uint32_t initialSendRate; // Initial Send Rate
|
||||
int32_t fixedRate; // Fixed Send Rate
|
||||
int32_t timingStop; // Timing Stop(-1 indicator infinity)
|
||||
int32_t delayGreedy; // Start greedy after the specified time (ms)
|
||||
int32_t greedyRate; // Greedy Send Rate(-1 indicator no greedy)
|
||||
uint32_t delayStart; // Delay Start(ms)
|
||||
};
|
||||
|
||||
class Consumer : noncopyable
|
||||
{
|
||||
public:
|
||||
Consumer(Face &face, const Options &options);
|
||||
|
||||
virtual ~Consumer(){}
|
||||
|
||||
signal::Signal<Consumer, uint64_t, Rtt> afterData;
|
||||
|
||||
signal::Signal<Consumer, uint64_t, Rtt, lp::NackHeader> afterNack;
|
||||
|
||||
signal::Signal<Consumer, uint64_t> afterTimeout;
|
||||
|
||||
signal::Signal<Consumer> afterFinish;
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
public:
|
||||
Name makeInterestName(uint64_t seq);
|
||||
|
||||
virtual void sendPacket();
|
||||
|
||||
virtual void onTimeout(uint64_t seq);
|
||||
|
||||
virtual void onData(const Data& data, uint64_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
virtual void onNack(const lp::Nack &nack, uint64_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
protected:
|
||||
virtual void scheduleNextPacket() = 0;
|
||||
|
||||
private:
|
||||
void traceRate();
|
||||
|
||||
protected:
|
||||
const Options &m_options;
|
||||
int m_nSent;
|
||||
uint64_t m_nextSeq;
|
||||
int64_t m_seqMax;
|
||||
int m_nOutstanding;
|
||||
Face &m_face;
|
||||
Scheduler m_scheduler;
|
||||
int m_stopFlag;
|
||||
|
||||
uint64_t m_recvBytes;
|
||||
uint64_t m_traceTimes;
|
||||
time::steady_clock::TimePoint m_startTime;
|
||||
/// @cond include_hidden
|
||||
/**
|
||||
* \struct This struct contains sequence numbers of packets to be retransmitted
|
||||
*/
|
||||
struct RetxSeqsContainer : public std::set<uint32_t>
|
||||
{
|
||||
};
|
||||
RetxSeqsContainer m_retxSeqs; ///< \brief ordered set of sequence numbers to be retransmitted
|
||||
scheduler::EventId m_nextInterestEvent;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "qsccp-consumer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
QsccpConsumer::QsccpConsumer(Face &face, const Options &options)
|
||||
: Consumer(face, options),
|
||||
m_firstTime(true),
|
||||
tos(options.tos),
|
||||
delayStart(options.delayStart),
|
||||
dsz(options.dsz),
|
||||
sendRate(options.initialSendRate),
|
||||
fixedRate(options.fixedRate),
|
||||
timingStop(options.timingStop),
|
||||
delayGreedy(options.delayGreedy),
|
||||
greedyRate(options.greedyRate),
|
||||
m_recvDataNum(0)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t QsccpConsumer::updateRate(uint64_t newRate)
|
||||
{
|
||||
if (this->fixedRate > 0)
|
||||
{
|
||||
return this->fixedRate;
|
||||
}
|
||||
if (this->sendRate == 0)
|
||||
{
|
||||
this->sendRate = newRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->sendRate = 0.8 * this->sendRate + 0.2 * newRate;
|
||||
}
|
||||
return this->sendRate;
|
||||
}
|
||||
|
||||
void QsccpConsumer::startGreedy()
|
||||
{
|
||||
this->fixedRate = this->greedyRate;
|
||||
this->sendRate = this->greedyRate;
|
||||
}
|
||||
|
||||
void QsccpConsumer::onData(const Data &data, uint64_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
// print tags
|
||||
auto targetRate = data.getTargetRate();
|
||||
// std::cout << "onData: " << seq << ", targetRate: " << *targetRate << std::endl;
|
||||
if (targetRate)
|
||||
{
|
||||
this->updateRate(*targetRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NS_LOG_DEBUG("no target rate");
|
||||
}
|
||||
|
||||
scheduleNextPacket();
|
||||
|
||||
Consumer::onData(data, seq, sendTime);
|
||||
|
||||
++this->m_recvDataNum;
|
||||
if (this->m_seqMax == this->m_recvDataNum)
|
||||
{
|
||||
this->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void QsccpConsumer::scheduleNextPacket()
|
||||
{
|
||||
if (m_stopFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->m_firstTime)
|
||||
{
|
||||
m_startTime = time::steady_clock::now();
|
||||
this->m_firstTime = false;
|
||||
|
||||
if (this->fixedRate > 0)
|
||||
{
|
||||
this->sendRate = fixedRate;
|
||||
}
|
||||
if (this->timingStop > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->timingStop), [this]
|
||||
{
|
||||
stop(); });
|
||||
}
|
||||
if (this->delayGreedy > 0 && this->greedyRate > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->delayGreedy), [this]
|
||||
{ startGreedy(); });
|
||||
}
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::milliseconds(this->delayStart), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
else if (!m_nextInterestEvent)
|
||||
{
|
||||
if (this->sendRate <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto waitTime = (this->dsz * 1000000000) / this->sendRate + 1;
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::nanoseconds(waitTime), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
}
|
||||
|
||||
void QsccpConsumer::sendPacket()
|
||||
{
|
||||
scheduleNextPacket();
|
||||
// send packet here
|
||||
uint32_t seq = std::numeric_limits<uint32_t>::max(); // invalid
|
||||
while (m_retxSeqs.size())
|
||||
{
|
||||
seq = *m_retxSeqs.begin();
|
||||
m_retxSeqs.erase(m_retxSeqs.begin());
|
||||
break;
|
||||
}
|
||||
|
||||
if (seq == std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_seqMax != std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_nextSeq >= m_seqMax)
|
||||
{
|
||||
stop();
|
||||
// finish();
|
||||
return; // we are totally done
|
||||
}
|
||||
}
|
||||
seq = m_nextSeq++;
|
||||
}
|
||||
|
||||
Interest interest(makeInterestName(seq));
|
||||
interest.setCanBePrefix(false);
|
||||
interest.setMustBeFresh(true);
|
||||
interest.setServiceClass(this->tos);
|
||||
interest.setDsz(this->dsz);
|
||||
interest.setInterestLifetime(m_options.lifetime);
|
||||
|
||||
auto now = time::steady_clock::now();
|
||||
m_face.expressInterest(interest,
|
||||
bind(&Consumer::onData, this, _2, seq, now),
|
||||
bind(&Consumer::onNack, this, _2, seq, now),
|
||||
bind(&Consumer::onTimeout, this, seq));
|
||||
|
||||
++m_nSent;
|
||||
++m_nOutstanding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class QsccpConsumer : public Consumer
|
||||
{
|
||||
public:
|
||||
explicit QsccpConsumer(Face &face, const Options &options);
|
||||
|
||||
void sendPacket() override;
|
||||
|
||||
void onData(const Data& data, uint64_t seq, const time::steady_clock::TimePoint &sendTime) override;
|
||||
|
||||
protected:
|
||||
void scheduleNextPacket() override;
|
||||
|
||||
void startGreedy();
|
||||
|
||||
private:
|
||||
uint64_t updateRate(uint64_t newRate);
|
||||
|
||||
private:
|
||||
bool m_firstTime = true;
|
||||
// private attribute here
|
||||
uint64_t tos;
|
||||
uint64_t delayStart;
|
||||
uint64_t dsz;
|
||||
uint64_t sendRate;
|
||||
int32_t fixedRate;
|
||||
int32_t timingStop;
|
||||
int32_t delayGreedy;
|
||||
int32_t greedyRate;
|
||||
uint64_t m_recvDataNum;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
Consumer::Consumer(Face &face, const Options &options)
|
||||
: m_options(options),
|
||||
m_nSent(0),
|
||||
m_nextSeq(options.startSeq),
|
||||
m_seqMax(options.seqMax),
|
||||
m_nOutstanding(0),
|
||||
m_face(face),
|
||||
m_scheduler(m_face.getIoService()),
|
||||
m_stopFlag(false),
|
||||
m_recvBytes(0),
|
||||
m_traceTimes(0),
|
||||
m_firstTime(true)
|
||||
{
|
||||
if (m_options.seqMax < 0)
|
||||
{
|
||||
m_seqMax = std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(50),
|
||||
bind(&Consumer::checkRetxTimeout, this));
|
||||
}
|
||||
|
||||
void Consumer::traceRate()
|
||||
{
|
||||
if (!m_stopFlag)
|
||||
{
|
||||
m_scheduler.schedule(
|
||||
time::milliseconds(500),
|
||||
bind(&Consumer::traceRate, this));
|
||||
}
|
||||
|
||||
m_traceTimes++;
|
||||
// print rate
|
||||
std::cout << "cc:rate:<" << m_traceTimes << "," << m_recvBytes << ">" << m_nSent << std::endl;
|
||||
m_recvBytes = 0;
|
||||
m_nSent = 0;
|
||||
}
|
||||
|
||||
void Consumer::start()
|
||||
{
|
||||
m_stopFlag = false;
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void Consumer::stop()
|
||||
{
|
||||
m_nextInterestEvent.cancel();
|
||||
m_retxEvent.cancel();
|
||||
m_stopFlag = true;
|
||||
}
|
||||
|
||||
void Consumer::checkRetxTimeout()
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
|
||||
auto rto = m_rtt.RetransmitTimeout();
|
||||
|
||||
std::cout << "RTO: " << m_rtt.duration_to_second_double(rto) << std::endl;
|
||||
|
||||
while (!m_seqTimeouts.empty())
|
||||
{
|
||||
SeqTimeoutsContainer::index<i_timestamp>::type::iterator entry =
|
||||
m_seqTimeouts.get<i_timestamp>().begin();
|
||||
if (entry->time + rto <= now) // timeout expired?
|
||||
{
|
||||
// std::cout << "RTOTIMEOUT: " << entry->seq << std::endl;
|
||||
uint32_t seqNo = entry->seq;
|
||||
if (m_seqRetxCounts.find(seqNo) != m_seqRetxCounts.end())
|
||||
{
|
||||
m_seqTimeouts.get<i_timestamp>().erase(entry);
|
||||
onTimeout(seqNo);
|
||||
}
|
||||
}
|
||||
else
|
||||
break; // nothing else to do. All later packets need not be retransmitted
|
||||
}
|
||||
|
||||
m_retxEvent = m_scheduler.schedule(time::milliseconds(50), bind(&Consumer::checkRetxTimeout, this));
|
||||
}
|
||||
|
||||
void Consumer::sendPacket()
|
||||
{
|
||||
uint32_t seq = std::numeric_limits<uint32_t>::max(); // invalid
|
||||
while (m_retxSeqs.size())
|
||||
{
|
||||
seq = *m_retxSeqs.begin();
|
||||
m_retxSeqs.erase(m_retxSeqs.begin());
|
||||
break;
|
||||
}
|
||||
|
||||
if (seq == std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_seqMax != std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
if (m_nextSeq >= m_seqMax)
|
||||
{
|
||||
return; // we are totally done
|
||||
}
|
||||
}
|
||||
seq = m_nextSeq++;
|
||||
}
|
||||
|
||||
Interest interest(makeInterestName(seq));
|
||||
interest.setCanBePrefix(false);
|
||||
interest.setMustBeFresh(true);
|
||||
interest.setServiceClass(5);
|
||||
interest.setInterestLifetime(m_options.lifetime);
|
||||
|
||||
auto now = time::steady_clock::now();
|
||||
m_face.expressInterest(interest,
|
||||
bind(&Consumer::onData, this, _2, seq, now),
|
||||
bind(&Consumer::onNack, this, _2, seq, now),
|
||||
bind(&Consumer::onTimeout, this, seq));
|
||||
|
||||
willSendInterest(seq);
|
||||
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void Consumer::onTimeout(uint32_t seq)
|
||||
{
|
||||
std::cout << "onTimeout: " << seq << std::endl;
|
||||
afterTimeout(seq);
|
||||
|
||||
m_rtt.IncreaseMultiplier(); // Double the next RTO
|
||||
m_rtt.SentSeq(seq, 1); // make sure to disable RTT calculation for this sample
|
||||
m_retxSeqs.insert(seq);
|
||||
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
auto now = time::steady_clock::now();
|
||||
std::cout << "cc:delay:<" << (now - m_startTime).count() << "," << seq << "," << (now - sendTime).count() << ">" << std::endl;
|
||||
|
||||
afterData(seq, now - sendTime);
|
||||
if (m_seqRetxCounts.find(seq) != m_seqRetxCounts.end())
|
||||
{
|
||||
m_recvBytes += data.wireEncode().size();
|
||||
m_seqRetxCounts.erase(seq);
|
||||
}
|
||||
|
||||
m_seqFullDelay.erase(seq);
|
||||
m_seqLastDelay.erase(seq);
|
||||
|
||||
m_seqTimeouts.erase(seq);
|
||||
m_retxSeqs.erase(seq);
|
||||
|
||||
m_rtt.AckSeq(seq);
|
||||
// finish();
|
||||
}
|
||||
|
||||
void Consumer::onNack(const lp::Nack &nack, uint32_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
std::cout << "onNack: " << nack.getReason() << std::endl;
|
||||
afterNack(seq, time::steady_clock::now() - sendTime, nack.getHeader());
|
||||
scheduleNextPacket();
|
||||
// finish();
|
||||
}
|
||||
|
||||
Name Consumer::makeInterestName(uint32_t seq)
|
||||
{
|
||||
return Name(m_options.prefix).appendNumber(seq);
|
||||
}
|
||||
|
||||
void Consumer::willSendInterest(uint32_t seq)
|
||||
{
|
||||
++m_nSent;
|
||||
m_seqTimeouts.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
m_seqFullDelay.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
|
||||
m_seqLastDelay.erase(seq);
|
||||
m_seqLastDelay.insert(SeqTimeout(seq, time::steady_clock::now()));
|
||||
|
||||
m_seqRetxCounts[seq]++;
|
||||
|
||||
m_rtt.SentSeq(seq, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef NDN_CONSUMER_H
|
||||
#define NDN_CONSUMER_H
|
||||
#include "core/common.hpp"
|
||||
#include "ndn-rtt-mean-deviation.hpp"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/tag.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
typedef time::duration<double, time::milliseconds::period> Rtt;
|
||||
|
||||
enum CcAlgorithm
|
||||
{
|
||||
AIMD,
|
||||
BIC,
|
||||
CUBIC
|
||||
};
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
Name prefix;
|
||||
uint32_t startSeq; // Initial sequence number
|
||||
time::milliseconds lifetime; // Lifetime of Interest
|
||||
int64_t seqMax; // Max sequence number
|
||||
uint32_t tos; // Type of Service
|
||||
uint32_t dsz; // Data size
|
||||
int32_t reqNum; // Request Data Num(-1 indicator infinity)
|
||||
uint32_t initialSendRate; // Initial Send Rate
|
||||
int32_t fixedRate; // Fixed Send Rate
|
||||
int32_t timingStop; // Timing Stop(-1 indicator infinity)
|
||||
int32_t delayGreedy; // Start greedy after the specified time (ms)
|
||||
int32_t greedyRate; // Greedy Send Rate(-1 indicator no greedy)
|
||||
uint32_t delayStart; // Delay Start(ms)
|
||||
|
||||
std::string schema; // QSCCP or PCON
|
||||
uint32_t initialWindowSize = 1; // Initial Window Size
|
||||
bool setInitialWindowOnTimeout = false; // Set initial window size on timeout
|
||||
|
||||
CcAlgorithm ccAlgorithm = AIMD; // Specify which window adaptation algorithm to use (AIMD, BIC, or CUBIC)
|
||||
double beta = 0.5; // TCP Multiplicative Decrease factor
|
||||
double cubicBeta = 0.8; // TCP CUBIC Multiplicative Decrease factor
|
||||
double addRttSuppress = 0.5; // Minimum number of RTTs (1 + this factor) between window decreases
|
||||
bool reactToCongestionMarks = true; // React to congestion marks (ECN, CE)
|
||||
bool useCwa = true; // Use Congestion Window Acceleration (CWA) algorithm
|
||||
bool useCubicFastConv = true; // Use CUBIC fast convergence algorithm
|
||||
};
|
||||
|
||||
class Consumer : noncopyable
|
||||
{
|
||||
public:
|
||||
Consumer(Face &face, const Options &options);
|
||||
|
||||
virtual ~Consumer() {}
|
||||
|
||||
signal::Signal<Consumer, uint32_t, Rtt> afterData;
|
||||
|
||||
signal::Signal<Consumer, uint32_t, Rtt, lp::NackHeader> afterNack;
|
||||
|
||||
signal::Signal<Consumer, uint32_t> afterTimeout;
|
||||
|
||||
signal::Signal<Consumer> afterFinish;
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
public:
|
||||
Name makeInterestName(uint32_t seq);
|
||||
|
||||
virtual void sendPacket();
|
||||
|
||||
virtual void onTimeout(uint32_t seq);
|
||||
|
||||
virtual void onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
virtual void onNack(const lp::Nack &nack, uint32_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
virtual void willSendInterest(uint32_t seq);
|
||||
|
||||
protected:
|
||||
virtual void scheduleNextPacket() = 0;
|
||||
|
||||
/**
|
||||
* \brief Checks if the packet need to be retransmitted becuase of retransmission timer expiration
|
||||
*/
|
||||
void
|
||||
checkRetxTimeout();
|
||||
|
||||
private:
|
||||
void traceRate();
|
||||
|
||||
protected:
|
||||
const Options &m_options;
|
||||
int m_nSent;
|
||||
uint32_t m_nextSeq;
|
||||
int64_t m_seqMax;
|
||||
int m_nOutstanding;
|
||||
Face &m_face;
|
||||
Scheduler m_scheduler;
|
||||
int m_stopFlag;
|
||||
|
||||
uint64_t m_recvBytes;
|
||||
uint64_t m_traceTimes;
|
||||
time::steady_clock::TimePoint m_startTime;
|
||||
scheduler::EventId m_nextInterestEvent;
|
||||
bool m_firstTime = true;
|
||||
|
||||
/// @cond include_hidden
|
||||
/**
|
||||
* \struct This struct contains sequence numbers of packets to be retransmitted
|
||||
*/
|
||||
struct RetxSeqsContainer : public std::set<uint32_t>
|
||||
{
|
||||
};
|
||||
|
||||
RetxSeqsContainer m_retxSeqs; ///< \brief ordered set of sequence numbers to be retransmitted
|
||||
|
||||
RttMeanDeviation m_rtt;
|
||||
scheduler::EventId m_retxEvent;
|
||||
// RttEstimator m_rtt;
|
||||
/**
|
||||
* \struct This struct contains a pair of packet sequence number and its timeout
|
||||
*/
|
||||
struct SeqTimeout
|
||||
{
|
||||
SeqTimeout(uint32_t _seq, time::steady_clock::TimePoint _time)
|
||||
: seq(_seq), time(_time)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t seq;
|
||||
time::steady_clock::TimePoint time;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond include_hidden
|
||||
class i_seq
|
||||
{
|
||||
};
|
||||
class i_timestamp
|
||||
{
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond include_hidden
|
||||
/**
|
||||
* \struct This struct contains a multi-index for the set of SeqTimeout structs
|
||||
*/
|
||||
struct SeqTimeoutsContainer
|
||||
: public boost::multi_index::
|
||||
multi_index_container<SeqTimeout,
|
||||
boost::multi_index::
|
||||
indexed_by<boost::multi_index::
|
||||
ordered_unique<boost::multi_index::tag<i_seq>,
|
||||
boost::multi_index::
|
||||
member<SeqTimeout, uint32_t,
|
||||
&SeqTimeout::seq>>,
|
||||
boost::multi_index::
|
||||
ordered_non_unique<boost::multi_index::
|
||||
tag<i_timestamp>,
|
||||
boost::multi_index::
|
||||
member<SeqTimeout, time::steady_clock::TimePoint,
|
||||
&SeqTimeout::time>>>>
|
||||
{
|
||||
};
|
||||
|
||||
SeqTimeoutsContainer m_seqTimeouts; ///< \brief multi-index for the set of SeqTimeout structs
|
||||
|
||||
SeqTimeoutsContainer m_seqLastDelay;
|
||||
SeqTimeoutsContainer m_seqFullDelay;
|
||||
std::map<uint32_t, uint32_t> m_seqRetxCounts;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* RTT_ESTIMATOR_H */
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "ndn-rtt-estimator.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
void
|
||||
RttEstimator::SetMinRto(time::steady_clock::duration minRto)
|
||||
{
|
||||
m_minRto = minRto;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetMinRto(void) const
|
||||
{
|
||||
return m_minRto;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SetMaxRto(time::steady_clock::duration maxRto)
|
||||
{
|
||||
m_maxRto = maxRto;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetMaxRto(void) const
|
||||
{
|
||||
return m_maxRto;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SetCurrentEstimate(time::steady_clock::duration estimate)
|
||||
{
|
||||
m_currentEstimatedRtt = estimate;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::GetCurrentEstimate(void) const
|
||||
{
|
||||
return m_currentEstimatedRtt;
|
||||
}
|
||||
|
||||
// RttHistory methods
|
||||
RttHistory::RttHistory(uint32_t s, uint32_t c, time::steady_clock::TimePoint t)
|
||||
: seq(s), count(c), time(t), retx(false)
|
||||
{
|
||||
}
|
||||
|
||||
RttHistory::RttHistory(const RttHistory &h)
|
||||
: seq(h.seq), count(h.count), time(h.time), retx(h.retx)
|
||||
{
|
||||
}
|
||||
|
||||
// Base class methods
|
||||
RttEstimator::RttEstimator()
|
||||
: m_next(1),
|
||||
m_maxMultiplier(64),
|
||||
m_initialEstimatedRtt(second_double_to_duration(1)), // 1 seconds
|
||||
m_minRto(second_double_to_duration(0.2)), // 0.2 seconds
|
||||
m_maxRto(second_double_to_duration(200)), // 200 seconds
|
||||
m_nSamples(0),
|
||||
m_multiplier(1),
|
||||
m_history()
|
||||
{
|
||||
m_currentEstimatedRtt = m_initialEstimatedRtt;
|
||||
}
|
||||
|
||||
RttEstimator::RttEstimator(const RttEstimator &c)
|
||||
: m_next(c.m_next), m_maxMultiplier(c.m_maxMultiplier), m_initialEstimatedRtt(c.m_initialEstimatedRtt), m_currentEstimatedRtt(c.m_currentEstimatedRtt), m_minRto(c.m_minRto), m_maxRto(c.m_maxRto), m_nSamples(c.m_nSamples), m_multiplier(c.m_multiplier), m_history(c.m_history)
|
||||
{
|
||||
}
|
||||
|
||||
RttEstimator::~RttEstimator()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::SentSeq(uint32_t seq, uint32_t size)
|
||||
{
|
||||
// Note that a particular sequence has been sent
|
||||
if (seq == m_next)
|
||||
{ // This is the next expected one, just log at end
|
||||
m_history.push_back(RttHistory(seq, size, time::steady_clock::now()));
|
||||
m_next = seq + uint32_t(size); // Update next expected
|
||||
}
|
||||
else
|
||||
{ // This is a retransmit, find in list and mark as re-tx
|
||||
for (RttHistory_t::iterator i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if ((seq >= i->seq) && (seq < (i->seq + uint32_t(i->count))))
|
||||
{ // Found it
|
||||
i->retx = true;
|
||||
// One final test..be sure this re-tx does not extend "next"
|
||||
if ((seq + uint32_t(size)) > m_next)
|
||||
{
|
||||
m_next = seq + uint32_t(size);
|
||||
i->count = ((seq + uint32_t(size)) - i->seq); // And update count in hist
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttEstimator::AckSeq(uint32_t ackSeq)
|
||||
{
|
||||
// An ack has been received, calculate rtt and log this measurement
|
||||
// Note we use a linear search (O(n)) for this since for the common
|
||||
// case the ack'ed packet will be at the head of the list
|
||||
time::steady_clock::duration m = time::steady_clock::duration::zero();
|
||||
if (m_history.size() == 0)
|
||||
{
|
||||
return (m); // No pending history, just exit
|
||||
}
|
||||
RttHistory &h = m_history.front();
|
||||
if (!h.retx && ackSeq >= (h.seq + uint32_t(h.count)))
|
||||
{ // Ok to use this sample
|
||||
m = time::steady_clock::now() - h.time; // Elapsed time::nanoseconds
|
||||
Measurement(m); // Log the measurement
|
||||
ResetMultiplier(); // Reset multiplier on valid measurement
|
||||
}
|
||||
// Now delete all ack history with seq <= ack
|
||||
while (m_history.size() > 0)
|
||||
{
|
||||
RttHistory &h = m_history.front();
|
||||
if ((h.seq + uint32_t(h.count)) > ackSeq)
|
||||
break; // Done removing
|
||||
m_history.pop_front(); // Remove
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::ClearSent()
|
||||
{
|
||||
// Clear all history entries
|
||||
m_next = 1;
|
||||
m_history.clear();
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::IncreaseMultiplier()
|
||||
{
|
||||
m_multiplier = (m_multiplier * 2 < m_maxMultiplier) ? m_multiplier * 2 : m_maxMultiplier;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::ResetMultiplier()
|
||||
{
|
||||
m_multiplier = 1;
|
||||
}
|
||||
|
||||
void
|
||||
RttEstimator::Reset()
|
||||
{
|
||||
// Reset to initial state
|
||||
m_next = 1;
|
||||
m_currentEstimatedRtt = m_initialEstimatedRtt;
|
||||
m_history.clear(); // Remove all info from the history
|
||||
m_nSamples = 0;
|
||||
ResetMultiplier();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#ifndef NDN_RTT_ESTIMATOR_H
|
||||
#define NDN_RTT_ESTIMATOR_H
|
||||
#include "core/common.hpp"
|
||||
#include <deque>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
/**
|
||||
* \ingroup ndn-apps
|
||||
*
|
||||
* \brief Helper class to store RTT measurements
|
||||
*/
|
||||
class RttHistory
|
||||
{
|
||||
public:
|
||||
RttHistory(uint32_t seq, uint32_t size, time::steady_clock::TimePoint time);
|
||||
RttHistory(const RttHistory &h); // Copy constructor
|
||||
public:
|
||||
uint32_t seq; // First sequence number in packet sent
|
||||
uint32_t count; // Number of bytes sent
|
||||
time::steady_clock::TimePoint time; // Time this one was sent
|
||||
bool retx; // True if this has been retransmitted
|
||||
};
|
||||
|
||||
typedef std::deque<RttHistory> RttHistory_t;
|
||||
|
||||
/**
|
||||
* \ingroup tcp
|
||||
*
|
||||
* \brief Base class for all RTT Estimators
|
||||
*/
|
||||
class RttEstimator
|
||||
{
|
||||
public:
|
||||
RttEstimator();
|
||||
RttEstimator(const RttEstimator &);
|
||||
|
||||
virtual ~RttEstimator();
|
||||
|
||||
/**
|
||||
* \brief Note that a particular sequence has been sent
|
||||
* \param seq the packet sequence number.
|
||||
* \param size the packet size.
|
||||
*/
|
||||
virtual void
|
||||
SentSeq(uint32_t seq, uint32_t size);
|
||||
|
||||
/**
|
||||
* \brief Note that a particular ack sequence has been received
|
||||
* \param ackSeq the ack sequence number.
|
||||
* \return The measured RTT for this ack.
|
||||
*/
|
||||
virtual time::steady_clock::duration
|
||||
AckSeq(uint32_t ackSeq);
|
||||
|
||||
/**
|
||||
* \brief Clear all history entries
|
||||
*/
|
||||
virtual void
|
||||
ClearSent();
|
||||
|
||||
/**
|
||||
* \brief Add a new measurement to the estimator. Pure virtual function.
|
||||
* \param t the new RTT measure.
|
||||
*/
|
||||
virtual void
|
||||
Measurement(time::steady_clock::duration t) = 0;
|
||||
|
||||
/**
|
||||
* \brief Returns the estimated RTO. Pure virtual function.
|
||||
* \return the estimated RTO.
|
||||
*/
|
||||
virtual time::steady_clock::duration
|
||||
RetransmitTimeout() = 0;
|
||||
|
||||
/**
|
||||
* \brief Increase the estimation multiplier up to MaxMultiplier.
|
||||
*/
|
||||
virtual void
|
||||
IncreaseMultiplier();
|
||||
|
||||
/**
|
||||
* \brief Resets the estimation multiplier to 1.
|
||||
*/
|
||||
virtual void
|
||||
ResetMultiplier();
|
||||
|
||||
/**
|
||||
* \brief Resets the estimation to its initial state.
|
||||
*/
|
||||
virtual void
|
||||
Reset();
|
||||
|
||||
/**
|
||||
* \brief Sets the Minimum RTO.
|
||||
* \param minRto The minimum RTO returned by the estimator.
|
||||
*/
|
||||
void
|
||||
SetMinRto(time::steady_clock::duration minRto);
|
||||
|
||||
/**
|
||||
* \brief Get the Minimum RTO.
|
||||
* \return The minimum RTO returned by the estimator.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetMinRto(void) const;
|
||||
|
||||
/**
|
||||
* \brief Sets the Maximum RTO.
|
||||
* \param minRto The maximum RTO returned by the estimator.
|
||||
*/
|
||||
void
|
||||
SetMaxRto(time::steady_clock::duration maxRto);
|
||||
|
||||
/**
|
||||
* \brief Get the Maximum RTO.
|
||||
* \return The maximum RTO returned by the estimator.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetMaxRto(void) const;
|
||||
|
||||
/**
|
||||
* \brief Sets the current RTT estimate (forcefully).
|
||||
* \param estimate The current RTT estimate.
|
||||
*/
|
||||
void
|
||||
SetCurrentEstimate(time::steady_clock::duration estimate);
|
||||
|
||||
/**
|
||||
* \brief gets the current RTT estimate.
|
||||
* \return The current RTT estimate.
|
||||
*/
|
||||
time::steady_clock::duration
|
||||
GetCurrentEstimate(void) const;
|
||||
|
||||
double
|
||||
duration_to_second_double(time::steady_clock::duration d) {
|
||||
return d.count() / 1000000000.0;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
second_double_to_duration(double d) {
|
||||
return time::steady_clock::duration(static_cast<long long>(d * 1000000000.0));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_next; // Next expected sequence to be sent
|
||||
uint16_t m_maxMultiplier;
|
||||
time::steady_clock::duration m_initialEstimatedRtt;
|
||||
|
||||
protected:
|
||||
time::steady_clock::duration m_currentEstimatedRtt; // Current estimate
|
||||
time::steady_clock::duration m_minRto; // minimum value of the timeout
|
||||
time::steady_clock::duration m_maxRto; // maximum value of the timeout
|
||||
uint32_t m_nSamples; // Number of samples
|
||||
uint16_t m_multiplier; // RTO Multiplier
|
||||
RttHistory_t m_history; // List of sent packet
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* RTT_ESTIMATOR_H */
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "ndn-rtt-mean-deviation.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
RttMeanDeviation::RttMeanDeviation()
|
||||
: m_gain(0.125),
|
||||
m_gain2(0.25),
|
||||
m_variance(0)
|
||||
{
|
||||
}
|
||||
|
||||
RttMeanDeviation::RttMeanDeviation(const RttMeanDeviation &c)
|
||||
: RttEstimator(c), m_gain(c.m_gain), m_gain2(c.m_gain2), m_variance(c.m_variance)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Measurement(time::steady_clock::duration m)
|
||||
{
|
||||
if (m_nSamples)
|
||||
{ // Not first
|
||||
time::steady_clock::duration err(m - m_currentEstimatedRtt);
|
||||
double gErr = duration_to_second_double(err) * m_gain;
|
||||
|
||||
m_currentEstimatedRtt += second_double_to_duration(gErr);
|
||||
|
||||
auto abs_err = err > time::steady_clock::duration::zero() ? err : -err;
|
||||
|
||||
time::steady_clock::duration difference = abs_err - m_variance;
|
||||
m_variance += second_double_to_duration(m_gain2 * duration_to_second_double(difference));
|
||||
}
|
||||
else
|
||||
{ // First sample
|
||||
m_currentEstimatedRtt = m; // Set estimate to current
|
||||
// variance = sample / 2; // And variance to current / 2
|
||||
// m_variance = m; // try this why????
|
||||
m_variance = second_double_to_duration(duration_to_second_double(m) / 2);
|
||||
}
|
||||
m_nSamples++;
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttMeanDeviation::RetransmitTimeout()
|
||||
{
|
||||
// std::cout << "RTO -> m_currentEstimatedRtt:" << duration_to_second_double(m_currentEstimatedRtt) << ", m_variance:" << duration_to_second_double(m_variance) << std::endl;
|
||||
double retval = std::min(
|
||||
duration_to_second_double(m_maxRto),
|
||||
std::max(
|
||||
m_multiplier * duration_to_second_double(m_minRto),
|
||||
m_multiplier * (duration_to_second_double(m_currentEstimatedRtt) + 4 * duration_to_second_double(m_variance))));
|
||||
return second_double_to_duration(retval);
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Reset()
|
||||
{
|
||||
// Reset to initial state
|
||||
m_variance = time::steady_clock::duration::zero();
|
||||
RttEstimator::Reset();
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::Gain(double g)
|
||||
{
|
||||
m_gain = g;
|
||||
}
|
||||
|
||||
void
|
||||
RttMeanDeviation::SentSeq(uint32_t seq, uint32_t size)
|
||||
{
|
||||
RttHistory_t::iterator i;
|
||||
for (i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if (seq == i->seq)
|
||||
{ // Found it
|
||||
i->retx = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that a particular sequence has been sent
|
||||
if (i == m_history.end())
|
||||
m_history.push_back(RttHistory(seq, size, time::steady_clock::now()));
|
||||
}
|
||||
|
||||
time::steady_clock::duration
|
||||
RttMeanDeviation::AckSeq(uint32_t ackSeq)
|
||||
{
|
||||
// An ack has been received, calculate rtt and log this measurement
|
||||
// Note we use a linear search (O(n)) for this since for the common
|
||||
// case the ack'ed packet will be at the head of the list
|
||||
auto m = time::steady_clock::duration::zero();
|
||||
if (m_history.size() == 0)
|
||||
return (m); // No pending history, just exit
|
||||
|
||||
for (RttHistory_t::iterator i = m_history.begin(); i != m_history.end(); ++i)
|
||||
{
|
||||
if (ackSeq == i->seq)
|
||||
{ // Found it
|
||||
if (!i->retx)
|
||||
{
|
||||
m = time::steady_clock::now() - i->time; // Elapsed time
|
||||
Measurement(m); // Log the measurement
|
||||
ResetMultiplier(); // Reset multiplier on valid measurement
|
||||
}
|
||||
m_history.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef NDN_RTT_MEAN_DEVIATION_H
|
||||
#define NDN_RTT_MEAN_DEVIATION_H
|
||||
|
||||
#include "ndn-rtt-estimator.hpp"
|
||||
|
||||
#endif /* NDN_RTT_MEAN_DEVIATION_H */
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
/**
|
||||
* \ingroup ndn-apps
|
||||
*
|
||||
* \brief The modified version of "Mean--Deviation" RTT estimator, as discussed by Van Jacobson that
|
||||
*better suits NDN communication model
|
||||
*
|
||||
* This class implements the "Mean--Deviation" RTT estimator, as discussed
|
||||
* by Van Jacobson and Michael J. Karels, in
|
||||
* "Congestion Avoidance and Control", SIGCOMM 88, Appendix A
|
||||
*
|
||||
*/
|
||||
class RttMeanDeviation : public RttEstimator
|
||||
{
|
||||
public:
|
||||
RttMeanDeviation();
|
||||
|
||||
RttMeanDeviation(const RttMeanDeviation &);
|
||||
|
||||
void
|
||||
SentSeq(uint32_t seq, uint32_t size);
|
||||
|
||||
time::steady_clock::duration
|
||||
AckSeq(uint32_t ackSeq);
|
||||
|
||||
void
|
||||
Measurement(time::steady_clock::duration measure);
|
||||
|
||||
time::steady_clock::duration
|
||||
RetransmitTimeout();
|
||||
|
||||
void
|
||||
Reset();
|
||||
|
||||
void
|
||||
Gain(double g);
|
||||
|
||||
private:
|
||||
double m_gain; // Filter gain
|
||||
double m_gain2; // Filter gain
|
||||
time::steady_clock::duration m_variance; // Current variance
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
#include "pcon-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
PconConsumer::PconConsumer(Face &face, const Options &options)
|
||||
: WindowConsumer(face, options),
|
||||
m_ccAlgorithm(options.ccAlgorithm),
|
||||
m_beta(options.beta),
|
||||
m_addRttSuppress(options.addRttSuppress),
|
||||
m_reactToCongestionMarks(options.reactToCongestionMarks),
|
||||
m_useCwa(options.useCwa),
|
||||
m_ssthresh(std::numeric_limits<double>::max()),
|
||||
m_highData(0),
|
||||
m_recPoint(0.0),
|
||||
m_useCubicFastConv(options.useCubicFastConv),
|
||||
m_cubicBeta(options.cubicBeta),
|
||||
m_cubicWmax(0),
|
||||
m_cubicLastWmax(0),
|
||||
m_cubicLastDecrease(time::steady_clock::now()),
|
||||
m_bicMinWin(0),
|
||||
m_bicMaxWin(std::numeric_limits<double>::max()),
|
||||
m_bicTargetWin(0),
|
||||
m_bicSsCwnd(0),
|
||||
m_bicSsTarget(0),
|
||||
m_isBicSs(false)
|
||||
{
|
||||
}
|
||||
|
||||
void PconConsumer::onTimeout(uint32_t seq)
|
||||
{
|
||||
WindowDecrease();
|
||||
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight--;
|
||||
}
|
||||
|
||||
Consumer::onTimeout(seq);
|
||||
}
|
||||
|
||||
void PconConsumer::onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
Consumer::onData(data, seq, sendTime);
|
||||
|
||||
// Set highest received Data to sequence number
|
||||
if (m_highData < seq)
|
||||
{
|
||||
m_highData = seq;
|
||||
}
|
||||
|
||||
if (data.getCongestionMark() > 0)
|
||||
{
|
||||
std::cout << "Congestion Mark received: " << seq << std::endl;
|
||||
if (m_reactToCongestionMarks)
|
||||
{
|
||||
WindowDecrease();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowIncrease();
|
||||
}
|
||||
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight--;
|
||||
}
|
||||
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void PconConsumer::WindowIncrease()
|
||||
{
|
||||
if (m_ccAlgorithm == CcAlgorithm::AIMD)
|
||||
{
|
||||
if (m_window < m_ssthresh)
|
||||
{
|
||||
m_window += 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window += (1.0 / m_window);
|
||||
}
|
||||
}
|
||||
else if (m_ccAlgorithm == CcAlgorithm::CUBIC)
|
||||
{
|
||||
CubicIncrease();
|
||||
}
|
||||
else if (m_ccAlgorithm == CcAlgorithm::BIC)
|
||||
{
|
||||
BicIncrease();
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT_MSG(false, "Unknown CC Algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
void PconConsumer::WindowDecrease()
|
||||
{
|
||||
if (!m_useCwa || m_highData > m_recPoint)
|
||||
{
|
||||
const double diff = m_nextSeq - m_highData;
|
||||
BOOST_ASSERT(diff > 0);
|
||||
|
||||
m_recPoint = m_nextSeq + (m_addRttSuppress * diff);
|
||||
|
||||
if (m_ccAlgorithm == CcAlgorithm::AIMD)
|
||||
{
|
||||
// Normal TCP Decrease:
|
||||
m_ssthresh = m_window * m_beta;
|
||||
m_window = m_ssthresh;
|
||||
}
|
||||
else if (m_ccAlgorithm == CcAlgorithm::CUBIC)
|
||||
{
|
||||
CubicDecrease();
|
||||
}
|
||||
else if (m_ccAlgorithm == CcAlgorithm::BIC)
|
||||
{
|
||||
BicDecrease();
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT_MSG(false, "Unknown CC Algorithm");
|
||||
}
|
||||
|
||||
// Window size cannot be reduced below initial size
|
||||
if (m_window < m_initialWindowSize)
|
||||
{
|
||||
m_window = m_initialWindowSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PconConsumer::BicIncrease()
|
||||
{
|
||||
if (m_window < BIC_LOW_WINDOW)
|
||||
{
|
||||
// Normal TCP AIMD behavior
|
||||
if (m_window < m_ssthresh)
|
||||
{
|
||||
m_window = m_window + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window = m_window + 1.0 / m_window;
|
||||
}
|
||||
}
|
||||
else if (!m_isBicSs)
|
||||
{
|
||||
// Binary increase
|
||||
if (m_bicTargetWin - m_window < BIC_MAX_INCREMENT)
|
||||
{ // Binary search
|
||||
m_window += (m_bicTargetWin - m_window) / m_window;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window += BIC_MAX_INCREMENT / m_window; // Additive increase
|
||||
}
|
||||
// FIX for equal double values.
|
||||
if (m_window + 0.00001 < m_bicMaxWin)
|
||||
{
|
||||
m_bicMinWin = m_window;
|
||||
m_bicTargetWin = (m_bicMaxWin + m_bicMinWin) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isBicSs = true;
|
||||
m_bicSsCwnd = 1;
|
||||
m_bicSsTarget = m_window + 1.0;
|
||||
m_bicMaxWin = std::numeric_limits<double>::max();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// BIC slow start
|
||||
m_window += m_bicSsCwnd / m_window;
|
||||
if (m_window >= m_bicSsTarget)
|
||||
{
|
||||
m_bicSsCwnd = 2 * m_bicSsCwnd;
|
||||
m_bicSsTarget = m_window + m_bicSsCwnd;
|
||||
}
|
||||
if (m_bicSsCwnd >= BIC_MAX_INCREMENT)
|
||||
{
|
||||
m_isBicSs = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PconConsumer::BicDecrease()
|
||||
{
|
||||
// BIC Decrease
|
||||
if (m_window >= BIC_LOW_WINDOW)
|
||||
{
|
||||
auto prev_max = m_bicMaxWin;
|
||||
m_bicMaxWin = m_window;
|
||||
m_window = m_window * m_cubicBeta;
|
||||
m_bicMinWin = m_window;
|
||||
if (prev_max > m_bicMaxWin)
|
||||
{
|
||||
// Fast Convergence
|
||||
m_bicMaxWin = (m_bicMaxWin + m_bicMinWin) / 2;
|
||||
}
|
||||
m_bicTargetWin = (m_bicMaxWin + m_bicMinWin) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal TCP Decrease:
|
||||
m_ssthresh = m_window * m_cubicBeta;
|
||||
m_window = m_ssthresh;
|
||||
}
|
||||
}
|
||||
|
||||
void PconConsumer::CubicIncrease()
|
||||
{
|
||||
// 1. Time since last congestion event in Seconds
|
||||
const double t = time::duration_cast<time::microseconds>(
|
||||
time::steady_clock::now() - m_cubicLastDecrease)
|
||||
.count() /
|
||||
1e6;
|
||||
|
||||
// 2. Time it takes to increase the window to cubic_wmax
|
||||
// K = cubic_root(W_max*(1-beta_cubic)/C) (Eq. 2)
|
||||
const double k = std::cbrt(m_cubicWmax * (1 - m_cubicBeta) / CUBIC_C);
|
||||
|
||||
// 3. Target: W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1)
|
||||
const double w_cubic = CUBIC_C * std::pow(t - k, 3) + m_cubicWmax;
|
||||
|
||||
// 4. Estimate of Reno Increase (Currently Disabled)
|
||||
// const double rtt = m_rtt->GetCurrentEstimate().GetSeconds();
|
||||
// const double w_est = m_cubic_wmax*m_beta + (3*(1-m_beta)/(1+m_beta)) * (t/rtt);
|
||||
constexpr double w_est = 0.0;
|
||||
|
||||
// Actual adaptation
|
||||
if (m_window < m_ssthresh)
|
||||
{
|
||||
m_window += 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(m_cubicWmax > 0);
|
||||
|
||||
double cubic_increment = std::max(w_cubic, w_est) - m_window;
|
||||
// Cubic increment must be positive:
|
||||
// Note: This change is not part of the RFC, but I added it to improve performance.
|
||||
if (cubic_increment < 0)
|
||||
{
|
||||
cubic_increment = 0.0;
|
||||
}
|
||||
m_window += cubic_increment / m_window;
|
||||
}
|
||||
}
|
||||
|
||||
void PconConsumer::CubicDecrease()
|
||||
{
|
||||
// This implementation is ported from https://datatracker.ietf.org/doc/rfc8312/
|
||||
const double FAST_CONV_DIFF = 1.0; // In percent
|
||||
|
||||
// A flow remembers the last value of W_max,
|
||||
// before it updates W_max for the current congestion event.
|
||||
|
||||
// Current w_max < last_wmax
|
||||
if (m_useCubicFastConv && m_window < m_cubicLastWmax * (1 - FAST_CONV_DIFF / 100))
|
||||
{
|
||||
m_cubicLastWmax = m_window;
|
||||
m_cubicWmax = m_window * (1.0 + m_cubicBeta) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save old cwnd as w_max:
|
||||
m_cubicLastWmax = m_window;
|
||||
m_cubicWmax = m_window;
|
||||
}
|
||||
|
||||
m_ssthresh = m_window * m_cubicBeta;
|
||||
m_ssthresh = std::max<double>(m_ssthresh, m_initialWindowSize);
|
||||
m_window = m_ssthresh;
|
||||
|
||||
m_cubicLastDecrease = time::steady_clock::now();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "window-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class PconConsumer : public WindowConsumer
|
||||
{
|
||||
public:
|
||||
explicit PconConsumer(Face &face, const Options &options);
|
||||
|
||||
virtual void onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
virtual void onTimeout(uint32_t seq);
|
||||
|
||||
private:
|
||||
void
|
||||
WindowIncrease();
|
||||
|
||||
void
|
||||
WindowDecrease();
|
||||
|
||||
void
|
||||
CubicIncrease();
|
||||
|
||||
void
|
||||
CubicDecrease();
|
||||
|
||||
void
|
||||
BicIncrease();
|
||||
|
||||
void
|
||||
BicDecrease();
|
||||
|
||||
private:
|
||||
CcAlgorithm m_ccAlgorithm;
|
||||
double m_beta;
|
||||
double m_addRttSuppress;
|
||||
bool m_reactToCongestionMarks;
|
||||
bool m_useCwa;
|
||||
|
||||
double m_ssthresh;
|
||||
uint32_t m_highData;
|
||||
double m_recPoint;
|
||||
|
||||
// TCP CUBIC Parameters //
|
||||
static constexpr double CUBIC_C = 0.4;
|
||||
bool m_useCubicFastConv;
|
||||
double m_cubicBeta;
|
||||
|
||||
double m_cubicWmax;
|
||||
double m_cubicLastWmax;
|
||||
time::steady_clock::TimePoint m_cubicLastDecrease;
|
||||
|
||||
// TCP BIC Parameters //
|
||||
//! Regular TCP behavior (including slow start) until this window size
|
||||
static constexpr uint32_t BIC_LOW_WINDOW = 14;
|
||||
|
||||
//! Sets the maximum (linear) increase of TCP BIC. Should be between 8 and 64.
|
||||
static constexpr uint32_t BIC_MAX_INCREMENT = 16;
|
||||
|
||||
// BIC variables:
|
||||
double m_bicMinWin; //!< last minimum cwnd
|
||||
double m_bicMaxWin; //!< last maximum cwnd
|
||||
double m_bicTargetWin;
|
||||
double m_bicSsCwnd;
|
||||
double m_bicSsTarget;
|
||||
bool m_isBicSs; //!< whether we are currently in the BIC slow start phase
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "core/common.hpp"
|
||||
#include "core/version.hpp"
|
||||
#include "pcon-consumer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class Runner : noncopyable
|
||||
{
|
||||
public:
|
||||
explicit Runner(const Options &options) : m_consumer(m_face, options)
|
||||
{
|
||||
m_consumer.afterFinish.connect([this]
|
||||
{ this->cancel(); });
|
||||
}
|
||||
|
||||
int
|
||||
run()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_consumer.start();
|
||||
m_face.processEvents();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
// m_tracer.onError(e.what());
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
cancel()
|
||||
{
|
||||
m_consumer.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
Face m_face;
|
||||
PconConsumer m_consumer;
|
||||
};
|
||||
|
||||
static void
|
||||
usage(const boost::program_options::options_description &options)
|
||||
{
|
||||
std::cout << "Usage: ndnping [options] ndn:/name/prefix\n"
|
||||
"\n"
|
||||
"Ping a NDN name prefix using Interests with name ndn:/name/prefix/ping/number.\n"
|
||||
"The numbers in the Interests are randomly generated unless specified.\n"
|
||||
"\n"
|
||||
<< options;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Options options;
|
||||
// options.shouldAllowStaleData = false;
|
||||
// options.nPings = -1;
|
||||
// options.interval = time::milliseconds(getDefaultPingInterval());
|
||||
// options.timeout = time::milliseconds(getDefaultPingTimeoutThreshold());
|
||||
// options.startSeq = 0;
|
||||
// options.shouldGenerateRandomSeq = true;
|
||||
// options.shouldPrintTimestamp = false;
|
||||
|
||||
// std::string identifier;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
po::options_description visibleOptDesc("Options");
|
||||
visibleOptDesc.add_options()("help,h", "print this message and exit")("version,V", "display version and exit");
|
||||
visibleOptDesc.add_options()(
|
||||
"startSeq", po::value<uint32_t>(&options.startSeq)->default_value(0), "start sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"seqMax", po::value<int64_t>(&options.seqMax)->default_value(-1), "maximum sequence number");
|
||||
visibleOptDesc.add_options()(
|
||||
"dsz", po::value<uint32_t>(&options.dsz)->default_value(8624), "data size");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayStart", po::value<uint32_t>(&options.delayStart)->default_value(0), "delay start time, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"fixedRate", po::value<int32_t>(&options.fixedRate)->default_value(-1), "fixed rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"timingStop", po::value<int32_t>(&options.timingStop)->default_value(-1), "timing stop, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"delayGreedy", po::value<int32_t>(&options.delayGreedy)->default_value(-1), "delay greedy, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"greedyRate", po::value<int32_t>(&options.greedyRate)->default_value(-1), "greedy rate, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"lifetime", po::value<time::milliseconds::rep>()->default_value(4000), "Interest lifetime, in milliseconds");
|
||||
visibleOptDesc.add_options()(
|
||||
"schema", po::value<std::string>(&options.schema)->default_value("QSCCP"), "Schema, QSCCP or PCON");
|
||||
visibleOptDesc.add_options()(
|
||||
"initialWindowSize", po::value<uint32_t>(&options.initialWindowSize)->default_value(1), "Initial Window Size");
|
||||
visibleOptDesc.add_options()(
|
||||
"setInitialWindowOnTimeout", po::value<bool>(&options.setInitialWindowOnTimeout)->default_value(false), "Set initial window size on timeout");
|
||||
visibleOptDesc.add_options()(
|
||||
"ccAlgorithm", po::value<std::string>()->default_value("BIC"), "Specify which window adaptation algorithm to use (AIMD, BIC, or CUBIC)");
|
||||
visibleOptDesc.add_options()(
|
||||
"beta", po::value<double>(&options.beta)->default_value(0.5), "TCP Multiplicative Decrease factor");
|
||||
visibleOptDesc.add_options()(
|
||||
"cubicBeta", po::value<double>(&options.cubicBeta)->default_value(0.8), "TCP CUBIC Multiplicative Decrease factor");
|
||||
visibleOptDesc.add_options()(
|
||||
"addRttSuppress", po::value<double>(&options.addRttSuppress)->default_value(0.5), "Minimum number of RTTs (1 + this factor) between window decreases");
|
||||
visibleOptDesc.add_options()(
|
||||
"reactToCongestionMarks", po::value<bool>(&options.reactToCongestionMarks)->default_value(false), "React to congestion marks (ECN, CE)");
|
||||
visibleOptDesc.add_options()(
|
||||
"useCwa", po::value<bool>(&options.useCwa)->default_value(false), "Use Congestion Window Acceleration (CWA) algorithm");
|
||||
visibleOptDesc.add_options()(
|
||||
"useCubicFastConv", po::value<bool>(&options.useCubicFastConv)->default_value(true), "Use CUBIC fast convergence algorithm");
|
||||
|
||||
po::options_description hiddenOptDesc;
|
||||
hiddenOptDesc.add_options()("prefix", po::value<std::string>(), "content prefix to request");
|
||||
|
||||
po::options_description optDesc;
|
||||
optDesc.add(visibleOptDesc).add(hiddenOptDesc);
|
||||
|
||||
po::positional_options_description optPos;
|
||||
optPos.add("prefix", -1);
|
||||
|
||||
try
|
||||
{
|
||||
po::variables_map optVm;
|
||||
po::store(po::command_line_parser(argc, argv).options(optDesc).positional(optPos).run(), optVm);
|
||||
po::notify(optVm);
|
||||
|
||||
if (optVm.count("help") > 0)
|
||||
{
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
if (optVm.count("version") > 0)
|
||||
{
|
||||
std::cout << "qsccp-client " << tools::VERSION << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (optVm.count("prefix") > 0)
|
||||
{
|
||||
options.prefix = Name(optVm["prefix"].as<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: No prefix specified" << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
if (optVm.count("ccAlgorithm") > 0)
|
||||
{
|
||||
std::string algorithm_str = optVm["ccAlgorithm"].as<std::string>();
|
||||
if (algorithm_str == "AIMD")
|
||||
{
|
||||
options.ccAlgorithm = CcAlgorithm::AIMD;
|
||||
}
|
||||
else if (algorithm_str == "BIC")
|
||||
{
|
||||
options.ccAlgorithm = CcAlgorithm::BIC;
|
||||
}
|
||||
else if (algorithm_str == "CUBIC")
|
||||
{
|
||||
options.ccAlgorithm = CcAlgorithm::CUBIC;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: Not support CC Algorithm: " << algorithm_str << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
options.lifetime = time::milliseconds(optVm["lifetime"].as<time::milliseconds::rep>());
|
||||
}
|
||||
catch (const po::error &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
usage(visibleOptDesc);
|
||||
}
|
||||
|
||||
std::cout << "PING " << options.prefix << std::endl;
|
||||
return Runner(options).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return ndn::cc::client::main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "window-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
WindowConsumer::WindowConsumer(Face &face, const Options &options)
|
||||
: Consumer(face, options),
|
||||
m_window(options.initialWindowSize),
|
||||
m_inFlight(0),
|
||||
m_initialWindowSize(options.initialWindowSize),
|
||||
m_setInitialWindowOnTimeout(options.setInitialWindowOnTimeout),
|
||||
fixedRate(options.fixedRate),
|
||||
delayStart(options.delayStart),
|
||||
timingStop(options.timingStop),
|
||||
delayGreedy(options.delayGreedy),
|
||||
greedyRate(options.greedyRate),
|
||||
dsz(options.dsz)
|
||||
{
|
||||
}
|
||||
|
||||
void WindowConsumer::startGreedy()
|
||||
{
|
||||
this->fixedRate = this->greedyRate;
|
||||
}
|
||||
|
||||
void WindowConsumer::scheduleNextPacket()
|
||||
{
|
||||
if (m_stopFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->m_firstTime)
|
||||
{
|
||||
this->m_firstTime = false;
|
||||
if (this->timingStop > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->timingStop), [this]
|
||||
{ stop(); });
|
||||
}
|
||||
if (this->delayGreedy > 0 && this->greedyRate > 0)
|
||||
{
|
||||
m_scheduler.schedule(time::milliseconds(this->delayGreedy), [this]
|
||||
{ startGreedy(); });
|
||||
}
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::milliseconds(this->delayStart), [this]
|
||||
{ sendPacket(); });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->fixedRate > 0)
|
||||
{
|
||||
auto waitTime = (this->dsz * 1000000000) / this->fixedRate + 1;
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::nanoseconds(waitTime), [this]
|
||||
{ sendPacket(); });
|
||||
return;
|
||||
}
|
||||
if (m_window == static_cast<uint32_t>(0))
|
||||
{
|
||||
m_nextInterestEvent.cancel();
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::milliseconds(1000), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
else if (m_inFlight >= m_window)
|
||||
{
|
||||
// simply do nothing
|
||||
}
|
||||
else if (!m_nextInterestEvent)
|
||||
{
|
||||
// if (m_nextInterestEvent)
|
||||
// {
|
||||
// std::cout << "???" << std::endl;
|
||||
// m_nextInterestEvent.cancel();
|
||||
// }
|
||||
// uint32_t waitTime = 1 / m_window * 1000000000;
|
||||
m_nextInterestEvent = m_scheduler.schedule(time::nanoseconds(0), [this]
|
||||
{ sendPacket(); });
|
||||
}
|
||||
}
|
||||
|
||||
void WindowConsumer::onTimeout(uint32_t seq)
|
||||
{
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight -= 1;
|
||||
}
|
||||
if (m_setInitialWindowOnTimeout)
|
||||
{
|
||||
m_window = m_initialWindowSize;
|
||||
}
|
||||
Consumer::onTimeout(seq);
|
||||
}
|
||||
|
||||
void WindowConsumer::onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime)
|
||||
{
|
||||
Consumer::onData(data, seq, sendTime);
|
||||
m_window++;
|
||||
if (m_inFlight > static_cast<uint32_t>(0))
|
||||
{
|
||||
m_inFlight--;
|
||||
}
|
||||
scheduleNextPacket();
|
||||
}
|
||||
|
||||
void WindowConsumer::willSendInterest(uint32_t seq)
|
||||
{
|
||||
|
||||
m_inFlight++;
|
||||
Consumer::willSendInterest(seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "ndn-consumer.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class WindowConsumer : public Consumer
|
||||
{
|
||||
public:
|
||||
explicit WindowConsumer(Face &face, const Options &options);
|
||||
|
||||
virtual void onData(const Data &data, uint32_t seq, const time::steady_clock::TimePoint &sendTime);
|
||||
|
||||
virtual void onTimeout(uint32_t seq);
|
||||
|
||||
virtual void willSendInterest(uint32_t seq);
|
||||
|
||||
protected:
|
||||
virtual void scheduleNextPacket();
|
||||
|
||||
void startGreedy();
|
||||
|
||||
protected:
|
||||
double m_window;
|
||||
uint32_t m_inFlight;
|
||||
|
||||
uint32_t m_initialWindowSize;
|
||||
bool m_setInitialWindowOnTimeout;
|
||||
|
||||
int32_t fixedRate;
|
||||
uint64_t delayStart;
|
||||
int32_t timingStop;
|
||||
int32_t delayGreedy;
|
||||
int32_t greedyRate;
|
||||
uint64_t dsz;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "ndn-producer.hpp"
|
||||
#include "core/common.hpp"
|
||||
#include "core/version.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace server
|
||||
{
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
class Runner : noncopyable
|
||||
{
|
||||
public:
|
||||
explicit Runner(const Options &options)
|
||||
: m_options(options),
|
||||
m_producer(m_face, m_keyChain, options)
|
||||
{
|
||||
m_producer.afterFinish.connect([this]
|
||||
{
|
||||
m_producer.stop();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
run()
|
||||
{
|
||||
std::cout << "Producer SERVER " << m_options.prefix << std::endl;
|
||||
|
||||
try
|
||||
{
|
||||
// Interest interest(Name("/A/1"));
|
||||
// interest.setCanBePrefix(false);
|
||||
// interest.setMustBeFresh(true);
|
||||
// interest.setServiceClass(0);
|
||||
// m_producer.onInterest(interest);
|
||||
m_producer.start();
|
||||
m_face.processEvents();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const Options &m_options;
|
||||
|
||||
Face m_face;
|
||||
KeyChain m_keyChain;
|
||||
Producer m_producer;
|
||||
};
|
||||
|
||||
static void
|
||||
usage(std::ostream &os, const std::string &programName, const po::options_description &options)
|
||||
{
|
||||
os << "Usage: " << programName << " [options] <prefix>\n"
|
||||
<< "\n"
|
||||
<< "Starts a NDN ping server that responds to Interests under name ndn:<prefix>/ping\n"
|
||||
<< "\n"
|
||||
<< options;
|
||||
}
|
||||
|
||||
static int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Options options;
|
||||
std::string prefix;
|
||||
|
||||
po::options_description visibleDesc("Options");
|
||||
visibleDesc.add_options()("help,h", "print this help message and exit");
|
||||
visibleDesc.add_options()("freshness,f",
|
||||
po::value<time::milliseconds::rep>()->default_value(4000),
|
||||
"FreshnessPeriod of the ping response, in milliseconds");
|
||||
visibleDesc.add_options()("size,s",
|
||||
po::value<uint32_t>()->default_value(1024),
|
||||
"size of response payload");
|
||||
visibleDesc.add_options()("version,V", "print program version and exit");
|
||||
|
||||
po::options_description hiddenDesc;
|
||||
hiddenDesc.add_options()("prefix", po::value<std::string>(&prefix));
|
||||
|
||||
po::options_description optDesc;
|
||||
optDesc.add(visibleDesc).add(hiddenDesc);
|
||||
|
||||
po::positional_options_description posDesc;
|
||||
posDesc.add("prefix", -1);
|
||||
|
||||
po::variables_map vm;
|
||||
try
|
||||
{
|
||||
po::store(po::command_line_parser(argc, argv).options(optDesc).positional(posDesc).run(), vm);
|
||||
po::notify(vm);
|
||||
}
|
||||
catch (const po::error &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << "\n\n";
|
||||
usage(std::cerr, argv[0], visibleDesc);
|
||||
return 2;
|
||||
}
|
||||
catch (const boost::bad_any_cast &e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << "\n\n";
|
||||
usage(std::cerr, argv[0], visibleDesc);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (vm.count("help") > 0)
|
||||
{
|
||||
usage(std::cout, argv[0], visibleDesc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count("version") > 0)
|
||||
{
|
||||
std::cout << "ndnpingserver " << tools::VERSION << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prefix.empty())
|
||||
{
|
||||
std::cerr << "ERROR: no name prefix specified\n\n";
|
||||
usage(std::cerr, argv[0], visibleDesc);
|
||||
return 2;
|
||||
}
|
||||
options.prefix = Name(prefix);
|
||||
|
||||
options.freshnessPeriod = time::milliseconds(vm["freshness"].as<time::milliseconds::rep>());
|
||||
if (vm.count("_deprecated_freshness_") > 0)
|
||||
{
|
||||
options.freshnessPeriod = time::milliseconds(vm["_deprecated_freshness_"].as<time::milliseconds::rep>());
|
||||
}
|
||||
if (options.freshnessPeriod < 0_ms)
|
||||
{
|
||||
std::cerr << "ERROR: FreshnessPeriod cannot be negative" << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
options.payloadSize = vm["size"].as<uint32_t>();
|
||||
|
||||
return Runner(options).run();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace ping
|
||||
} // namespace ndn
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return ndn::cc::server::main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "ndn-producer.hpp"
|
||||
#include <ndn-cxx/signature.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace server
|
||||
{
|
||||
Producer::Producer(Face &face, KeyChain &keyChain, const Options &options)
|
||||
: m_face(face),
|
||||
m_keyChain(keyChain),
|
||||
m_options(options),
|
||||
m_isRunning(false),
|
||||
m_signature(0)
|
||||
{
|
||||
auto b = make_shared<Buffer>();
|
||||
b->assign(m_options.payloadSize, 'a');
|
||||
m_payload = Block(tlv::Content, std::move(b));
|
||||
}
|
||||
|
||||
void Producer::run()
|
||||
{
|
||||
m_face.processEvents();
|
||||
}
|
||||
|
||||
void Producer::start()
|
||||
{
|
||||
if (m_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_isRunning = true;
|
||||
|
||||
m_face.setInterestFilter(m_options.prefix,
|
||||
bind(&Producer::onInterest, this, _2),
|
||||
[](const auto &, const auto &reason)
|
||||
{
|
||||
NDN_THROW(std::runtime_error("Failed to register prefix: " + reason));
|
||||
});
|
||||
}
|
||||
|
||||
void Producer::stop()
|
||||
{
|
||||
if (!m_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_isRunning = false;
|
||||
|
||||
m_face.setInterestFilter(m_options.prefix, nullptr);
|
||||
}
|
||||
|
||||
void Producer::onInterest(const Interest &interest)
|
||||
{
|
||||
afterReceive(interest.getName());
|
||||
|
||||
Data data(interest.getName());
|
||||
if (interest.getServiceClass())
|
||||
{
|
||||
int serviceClass = *interest.getServiceClass();
|
||||
data.setServiceClass(serviceClass);
|
||||
}
|
||||
// 设置 targetRate 为最大32位整数
|
||||
data.setTargetRate(std::numeric_limits<uint32_t>::max());
|
||||
data.setFreshnessPeriod(m_options.freshnessPeriod);
|
||||
data.setContent(m_payload);
|
||||
|
||||
Signature signature;
|
||||
SignatureInfo signatureInfo(static_cast<::ndn::tlv::SignatureTypeValue>(255));
|
||||
signature.setInfo(signatureInfo);
|
||||
signature.setValue(::ndn::makeNonNegativeIntegerBlock(::ndn::tlv::SignatureValue, m_signature));
|
||||
data.setSignature(signature);
|
||||
data.wireEncode();
|
||||
|
||||
// m_keyChain.sign(data);
|
||||
m_face.put(data);
|
||||
std::cout << "onInterest: " << interest.getName() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "core/common.hpp"
|
||||
|
||||
namespace ndn
|
||||
{
|
||||
namespace cc
|
||||
{
|
||||
namespace server
|
||||
{
|
||||
/**
|
||||
* @brief Options for NDN producer
|
||||
*/
|
||||
struct Options
|
||||
{
|
||||
Name prefix; //!< prefix to register
|
||||
time::milliseconds freshnessPeriod = 4_s; //!< data freshness period
|
||||
uint32_t payloadSize = 0; //!< response payload size (0 == no payload)
|
||||
};
|
||||
|
||||
class Producer : noncopyable
|
||||
{
|
||||
public:
|
||||
Producer(Face &face, KeyChain &keyChain, const Options &options);
|
||||
|
||||
/**
|
||||
* @brief Signals when Interest received
|
||||
*
|
||||
* @param name incoming interest name
|
||||
*/
|
||||
signal::Signal<Producer, Name> afterReceive;
|
||||
|
||||
/**
|
||||
* @brief Signals when finished pinging
|
||||
*/
|
||||
signal::Signal<Producer> afterFinish;
|
||||
|
||||
void
|
||||
run();
|
||||
|
||||
/**
|
||||
* @brief starts the Interest filter
|
||||
*
|
||||
* @note This method is non-blocking and caller need to call face.processEvents()
|
||||
*/
|
||||
void
|
||||
start();
|
||||
|
||||
/**
|
||||
* @brief Unregister set interest filter
|
||||
*/
|
||||
void
|
||||
stop();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Called when interest received
|
||||
*
|
||||
* @param interest incoming interest
|
||||
*/
|
||||
void
|
||||
onInterest(const Interest &interest);
|
||||
|
||||
private:
|
||||
const Options &m_options;
|
||||
Face &m_face;
|
||||
KeyChain &m_keyChain;
|
||||
Block m_payload;
|
||||
bool m_isRunning;
|
||||
uint32_t m_signature;
|
||||
RegisteredPrefixHandle m_registeredPrefix;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
top = '../..'
|
||||
|
||||
def build(bld):
|
||||
|
||||
bld.objects(
|
||||
target='qsccp-client-objects',
|
||||
source=bld.path.ant_glob('client/*.cpp', excl='client/main.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/qsccp-client',
|
||||
name='qsccp-client',
|
||||
source='client/main.cpp',
|
||||
use='qsccp-client-objects')
|
||||
|
||||
bld.objects(
|
||||
target='pcon-client-objects',
|
||||
source=bld.path.ant_glob('pcon/*.cpp', excl='pcon/pcon.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/pcon-client',
|
||||
name='pcon-client',
|
||||
source='pcon/pcon.cpp',
|
||||
use='pcon-client-objects')
|
||||
|
||||
bld.objects(
|
||||
target='bbr-client-objects',
|
||||
source=bld.path.ant_glob('bbr/*.cpp', excl='bbr/main.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/bbr-client',
|
||||
name='bbr-client',
|
||||
source='bbr/main.cpp',
|
||||
use='bbr-client-objects')
|
||||
|
||||
bld.objects(
|
||||
target='cc-server-objects',
|
||||
source=bld.path.ant_glob('server/*.cpp', excl='server/main.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/cc-producer',
|
||||
name='cc-producer',
|
||||
source='server/main.cpp',
|
||||
use='cc-server-objects')
|
||||
|
||||
## (for unit tests)
|
||||
|
||||
bld(target='cc-objects',
|
||||
use='qsccp-client-objects cc-server-objects')
|
||||
@@ -0,0 +1,42 @@
|
||||
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
top = '../..'
|
||||
|
||||
def build(bld):
|
||||
|
||||
bld.objects(
|
||||
target='qsccp-client-objects',
|
||||
source=bld.path.ant_glob('client/*.cpp', excl='client/main.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/qsccp-client',
|
||||
name='qsccp-client',
|
||||
source='client/main.cpp',
|
||||
use='qsccp-client-objects')
|
||||
|
||||
bld.objects(
|
||||
target='pcon-client-objects',
|
||||
source=bld.path.ant_glob('pcon/*.cpp', excl='pcon/pcon.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/pcon-client',
|
||||
name='pcon-client',
|
||||
source='pcon/pcon.cpp',
|
||||
use='pcon-client-objects')
|
||||
|
||||
bld.objects(
|
||||
target='cc-server-objects',
|
||||
source=bld.path.ant_glob('server/*.cpp', excl='server/main.cpp'),
|
||||
use='core-objects')
|
||||
|
||||
bld.program(
|
||||
target='../../bin/cc-producer',
|
||||
name='cc-producer',
|
||||
source='server/main.cpp',
|
||||
use='cc-server-objects')
|
||||
|
||||
## (for unit tests)
|
||||
|
||||
bld(target='cc-objects',
|
||||
use='qsccp-client-objects cc-server-objects')
|
||||
Reference in New Issue
Block a user