From 31e043da53b66cbe0976cdc2fc6bc7e6a1d8e141 Mon Sep 17 00:00:00 2001 From: David Brodsky Date: Thu, 19 Oct 2006 15:14:28 +0200 Subject: [PATCH] Add net-limit module for limiting network bandwidth. Tairon::Net::Limiter counts size of transferred data and permits more data to be transferred if there is free bandwidth. Tairon::Net::Reader is a base class for reading from socket accordingly to attached Limiter. This class cannot be used directly becuase its pointer to a buffer must be set. Tairon::Net::IReader is a subclass of Tairon::Net::Reader and creates internal buffer of specific size. Tairon::Net::LimitManager is used to reset Limiters every second so more transfer can occur. Note that limiting bandwidth at this network level isn't accurate because size of the packet header isn't included in the size of transferred data. --- include/tairon/net/Jamfile | 4 ++ include/tairon/net/ireader.h | 1 + include/tairon/net/limiter.h | 1 + include/tairon/net/limitmanager.h | 1 + include/tairon/net/reader.h | 1 + src/Jamfile | 1 + src/net-limit/Jamfile | 18 ++++++ src/net-limit/ireader.cpp | 44 ++++++++++++++ src/net-limit/ireader.h | 52 +++++++++++++++++ src/net-limit/limiter.cpp | 103 +++++++++++++++++++++++++++++++++ src/net-limit/limiter.h | 101 ++++++++++++++++++++++++++++++++ src/net-limit/limitmanager.cpp | 82 ++++++++++++++++++++++++++ src/net-limit/limitmanager.h | 89 ++++++++++++++++++++++++++++ src/net-limit/reader.cpp | 82 ++++++++++++++++++++++++++ src/net-limit/reader.h | 118 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 698 insertions(+) create mode 120000 include/tairon/net/ireader.h create mode 120000 include/tairon/net/limiter.h create mode 120000 include/tairon/net/limitmanager.h create mode 120000 include/tairon/net/reader.h create mode 100644 src/net-limit/Jamfile create mode 100644 src/net-limit/ireader.cpp create mode 100644 src/net-limit/ireader.h create mode 100644 src/net-limit/limiter.cpp create mode 100644 src/net-limit/limiter.h create mode 100644 src/net-limit/limitmanager.cpp create mode 100644 src/net-limit/limitmanager.h create mode 100644 src/net-limit/reader.cpp create mode 100644 src/net-limit/reader.h diff --git a/include/tairon/net/Jamfile b/include/tairon/net/Jamfile index ebdc7fa..a7559b9 100644 --- a/include/tairon/net/Jamfile +++ b/include/tairon/net/Jamfile @@ -2,8 +2,12 @@ SubDir TOP include tairon net ; InstallFile $(INCDIR)/tairon/net : httpclient.h + ireader.h + limiter.h + limitmanager.h loop.h poll.h + reader.h server.h socket.h socketnotifier.h diff --git a/include/tairon/net/ireader.h b/include/tairon/net/ireader.h new file mode 120000 index 0000000..807688f --- /dev/null +++ b/include/tairon/net/ireader.h @@ -0,0 +1 @@ +../../../src/net-limit/ireader.h \ No newline at end of file diff --git a/include/tairon/net/limiter.h b/include/tairon/net/limiter.h new file mode 120000 index 0000000..248294c --- /dev/null +++ b/include/tairon/net/limiter.h @@ -0,0 +1 @@ +../../../src/net-limit/limiter.h \ No newline at end of file diff --git a/include/tairon/net/limitmanager.h b/include/tairon/net/limitmanager.h new file mode 120000 index 0000000..fa6a533 --- /dev/null +++ b/include/tairon/net/limitmanager.h @@ -0,0 +1 @@ +../../../src/net-limit/limitmanager.h \ No newline at end of file diff --git a/include/tairon/net/reader.h b/include/tairon/net/reader.h new file mode 120000 index 0000000..47fff5e --- /dev/null +++ b/include/tairon/net/reader.h @@ -0,0 +1 @@ +../../../src/net-limit/reader.h \ No newline at end of file diff --git a/src/Jamfile b/src/Jamfile index 4dda1b1..097fb67 100644 --- a/src/Jamfile +++ b/src/Jamfile @@ -3,6 +3,7 @@ SubDir TOP src ; SubInclude TOP src core ; SubInclude TOP src crypto-sha1 ; SubInclude TOP src net-core ; +SubInclude TOP src net-limit ; SubInclude TOP src net-http ; SubInclude TOP src net-server ; SubInclude TOP src util-object ; diff --git a/src/net-limit/Jamfile b/src/net-limit/Jamfile new file mode 100644 index 0000000..ab01dab --- /dev/null +++ b/src/net-limit/Jamfile @@ -0,0 +1,18 @@ +SubDir TOP src net-limit ; + +SRCS = + ireader.cpp + limiter.cpp + limitmanager.cpp + reader.cpp + ; + +HEADERS = $(TOP)/include ; + +SHAREDLIBS = ; + +Shared libtairon-net-limit.so : $(SRCS) ; +MakeLocate libtairon-net-limit.so : $(TOP)/lib ; +InstallLib $(LIBDIR) : libtairon-net-limit.so ; + +# vim: syntax=jam ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/ireader.cpp b/src/net-limit/ireader.cpp new file mode 100644 index 0000000..9aab849 --- /dev/null +++ b/src/net-limit/ireader.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#include "ireader.h" + +namespace Tairon +{ + +namespace Net +{ + +/* {{{ IReader::IReader(size_t, Socket *, Limiter *) */ +IReader::IReader(size_t len, Socket *s, Limiter *l) : Reader(s, l) +{ + buffer = new char[len]; + bufSize = len; +} +/* }}} */ + +/* {{{ IReader::~IReader() */ +IReader::~IReader() +{ + delete [] buffer; +} +/* }}} */ + +}; // namespace Net + +}; // namespace Tairon + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/ireader.h b/src/net-limit/ireader.h new file mode 100644 index 0000000..1d84aeb --- /dev/null +++ b/src/net-limit/ireader.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#ifndef _tairon_net_limit_ireader_h +#define _tairon_net_limit_ireader_h + +#include "reader.h" + +namespace Tairon +{ + +namespace Net +{ + +/** \brief This class reads data from a socket into the internal buffer. + */ +class IReader : public Reader +{ + public: + /** Creates an IReader object. + * + * \param len Length of the internal buffer. + * \param s Socket used for reading. + * \param l Limiter used for limiting bandwidth. + */ + IReader(size_t len, Socket *s, Limiter *l); + + /** Destroys the object. + */ + virtual ~IReader(); +}; + +}; // namespace Net + +}; // namespace Tairon + +#endif + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/limiter.cpp b/src/net-limit/limiter.cpp new file mode 100644 index 0000000..687e2b3 --- /dev/null +++ b/src/net-limit/limiter.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#include "limiter.h" + +#include "limitmanager.h" + +namespace Tairon +{ + +namespace Net +{ + +// One may ask a question why we use a list of functors for clients that want +// to be notified about free bandwidth and not regular signal. It is because +// signal notifies all of its connected slots while we want to notify only +// those clients that can be satisfied. + +/* {{{ Limiter::Limiter(size_t) */ +Limiter::Limiter(size_t r, Limiter *p) : parent(p), rate(r) +{ + rest = rate; + + wantMoreFunctor = Tairon::Core::methodFunctor(this, &Limiter::reset); +} +/* }}} */ + +/* {{{ Limiter::~Limiter() */ +Limiter::~Limiter() +{ + if (parent) + parent->removeFromQueue(wantMoreFunctor); + else + LimitManager::self()->removeFromQueue(this); + + delete wantMoreFunctor; +} +/* }}} */ + +/* {{{ Limiter::getAllowedSize(size_t) */ +size_t Limiter::getAllowedSize(size_t max) +{ + if (rate > rest) + return rest; + return rate; +} +/* }}} */ + +/* {{{ Limiter::removeFromQueue(Tairon::Core::Functor0 *) */ +void Limiter::removeFromQueue(Tairon::Core::Functor0 *f) +{ + waiting.remove(f); +} +/* }}} */ + +/* {{{ Limiter::reset() */ +void Limiter::reset() +{ + rest = rate; + while (waiting.size() && (rest > 0)) { + (*waiting.front())(); + waiting.pop_front(); + } +} +/* }}} */ + +/* {{{ Limiter::transferred(size_t) */ +void Limiter::transferred(size_t s) +{ + rest -= s; +} +/* }}} */ + +/* {{{ Limiter::wantMore(Tairon::Core::Functor0 *) */ +void Limiter::wantMore(Tairon::Core::Functor0 *f) +{ + if (parent) + parent->wantMore(wantMoreFunctor); + else + LimitManager::self()->wantMore(this); + + waiting.push_back(f); +} +/* }}} */ + +}; // namespace Net + +}; // namespace Tairon + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/limiter.h b/src/net-limit/limiter.h new file mode 100644 index 0000000..ed6d1f0 --- /dev/null +++ b/src/net-limit/limiter.h @@ -0,0 +1,101 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#ifndef _tairon_net_limit_limiter_h +#define _tairon_net_limit_limiter_h + +#include + +#include + +namespace Tairon +{ + +namespace Net +{ + +/** \brief Limiter is used for limiting network bandwidth. + */ +class Limiter +{ + public: + /** Constructs a Limiter object. + * + * \param r Maximum number of bytes to by transferred per second. + * \param p Parent of this limiter. Transfer rate of this limiter won't + * exceed its parent's rate. If no parent is specified then this + * limiter notices its child about free bandwidth to use. An instance + * of Tairon::Net::LimitManager has to be created before Limiter can be + * used. + */ + Limiter(size_t r, Limiter *p = 0); + + /** Destroys the object. + */ + ~Limiter(); + + /** Returns maximum number of bytes that can be transferred. + */ + size_t getAllowedSize(size_t max); + + /** Removes a functor from queue of functors waiting for free transfer. + */ + void removeFromQueue(Tairon::Core::Functor0 *f); + + /** Sets number of transferred bytes to zero and calls waiting functors. + */ + void reset(); + + /** Informs this Limiter about transferred data. + */ + void transferred(size_t s); + + /** Someone wants to be notified when there is a possibility to transfer + * more data. + * + * \param f Functor that will be called when there are data to read. + */ + void wantMore(Tairon::Core::Functor0 *f); + + private: + /** Parent of this limiter. + */ + Limiter *parent; + + /** Maximum number of bytes to be transferred per second. + */ + size_t rate; + + /** Number of bytes that can be transferred. + */ + size_t rest; + + /** List of functors that will be called when a transfer can be done. + */ + std::list *> waiting; + + /** Functor for calling wantMore() method. + */ + Tairon::Core::Functor0 *wantMoreFunctor; +}; + +}; // namespace Net + +}; // namespace Tairon + +#endif + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/limitmanager.cpp b/src/net-limit/limitmanager.cpp new file mode 100644 index 0000000..9ccbc67 --- /dev/null +++ b/src/net-limit/limitmanager.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#include + +#include "limitmanager.h" + +#include "limiter.h" + +namespace Tairon +{ + +namespace Net +{ + +LimitManager *LimitManager::limitmanager = 0; + +/* {{{ LimitManager::LimitManager() */ +LimitManager::LimitManager() +{ + timer = new Timer(); + timer->timeoutSignal.connect(Tairon::Core::threadMethodDFunctor(Tairon::Core::Thread::current(), this, &LimitManager::timeout)); + timer->start(1000); // every second + + limitmanager = this; +} +/* }}} */ + +/* {{{ LimitManager::~LimitManager() */ +LimitManager::~LimitManager() +{ + timer->destroy(); +} +/* }}} */ + +/* {{{ LimitManager::removeFromQueue(Limiter *) */ +void LimitManager::removeFromQueue(Limiter *l) +{ + toDelete.push_back(l); +} +/* }}} */ + +/* {{{ LimitManager::timeout() */ +void LimitManager::timeout() +{ + // A crash due to an invalid iterator may be caused if a limiter wants to + // be removed from the queue while the queue is traversed. This workaround + // should do the right thing. + for (std::list::const_iterator it = toDelete.begin(); it != toDelete.end(); ++it) + waiting.erase(*it); + toDelete.clear(); + + for (std::set::const_iterator it = waiting.begin(); it != waiting.end(); ++it) + (*it)->reset(); +} +/* }}} */ + +/* {{{ LimitManager::wantMore(Limiter *) */ +void LimitManager::wantMore(Limiter *l) +{ + waiting.insert(l); +} +/* }}} */ + +}; // namespace Net + +}; // namespace Tairon + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/limitmanager.h b/src/net-limit/limitmanager.h new file mode 100644 index 0000000..bdf2b09 --- /dev/null +++ b/src/net-limit/limitmanager.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#ifndef _tairon_net_limit_limitmanager_h +#define _tairon_net_limit_limitmanager_h + +#include +#include + +namespace Tairon +{ + +namespace Net +{ + +class Limiter; +class Timer; + +/** \brief LimitManager handles list of limiters that are waiting for free + * bandwidth. + */ +class LimitManager +{ + public: + /** Creates a LimitManager object. + */ + LimitManager(); + + /** Destroys the manager. + */ + ~LimitManager(); + + /** Removes a limiter from the list. + */ + void removeFromQueue(Limiter *l); + + /** Returns pointer the instance of this class. + */ + static LimitManager *self() { + return limitmanager; + }; + + /** Adds a limiter to the list. + */ + void wantMore(Limiter *l); + + private: + /** Called every second by a timer. + */ + void timeout(); + + private: + /** Pointer to the instance of this class. + */ + static LimitManager *limitmanager; + + /** Obe second timer. + */ + Timer *timer; + + /** List of limiters that are about to be deleted from the queue. + */ + std::list toDelete; + + /** List of waiting limiters. + */ + std::set waiting; +}; + +}; // namespace Net + +}; // namespace Tairon + +#endif + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/reader.cpp b/src/net-limit/reader.cpp new file mode 100644 index 0000000..80c93e9 --- /dev/null +++ b/src/net-limit/reader.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#include +#include + +#include "reader.h" + +#include "limiter.h" + +namespace Tairon +{ + +namespace Net +{ + +/* {{{ Reader::Reader(Socket *) */ +Reader::Reader(Socket *s, Limiter *l) : limiter(l), offset(0), socket(s) +{ + readFunctor = Tairon::Core::methodFunctor(this, &Reader::read); +} +/* }}} */ + +/* {{{ Reader::~Reader() */ +Reader::~Reader() +{ + delete readFunctor; +} +/* }}} */ + +/* {{{ Reader::read() */ +void Reader::read() +{ + unsigned int count = limiter->getAllowedSize(bufSize - offset); + + if (!count) // no more bytes allowed to read + limiter->wantMore(readFunctor); + // it isn't needed to delete socket from polling here, because it's + // been already done + else { + try { + size_t len = socket->readTo(buffer + offset, count); + if (len) { + offset += len; + limiter->transferred(len); + dataReadSignal.emit(this, len); + if (offset == bufSize) + bufferFullSignal.emit(this); + } + } catch (const Tairon::Net::SocketException &) { // connection has been closed or something else bad happened + socket->close(); + errorSignal.emit(this, socket); + } + } +} +/* }}} */ + +/* {{{ Reader::reset() */ +void Reader::reset() +{ + offset = 0; +} +/* }}} */ + +}; // namespace Net + +}; // namespace Tairon + +// vim: ai sw=4 ts=4 noet fdm=marker diff --git a/src/net-limit/reader.h b/src/net-limit/reader.h new file mode 100644 index 0000000..89d4e0e --- /dev/null +++ b/src/net-limit/reader.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 David Brodsky * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation and appearing * + * in the file LICENSE.LGPL included in the packaging of this file. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Library General Public License for more details. * + * * + ***************************************************************************/ + +#ifndef _tairon_net_limit_reader_h +#define _tairon_net_limit_reader_h + +#include + +namespace Tairon +{ + +namespace Net +{ + +class Limiter; +class Socket; + +/** \brief This class provides base class for readers. + * + * Reader is an object that reads data from a socket and limits incoming + * bandwidth according to its Limiter. + * + * This class must not be used directly because the internal buffer must be set + * correctly. + */ +class Reader +{ + public: + /** Creates a Reader object. + * + * \param s Socket from which the Reader is reading. + * \param l Limiter used for limiting bandwidth. + */ + Reader(Socket *s, Limiter *l); + + /** Destroys the object. + */ + virtual ~Reader(); + + /** Returns pointer to the internal buffer. + */ + const char *getBuffer() { + return buffer; + }; + + /** Reads data from the socket. + */ + void read(); + + /** Sets offset to the internal buffer to zero. It is useful for + * reusing this reader. + */ + void reset(); + + public: + /** Emitted when the internal buffer is full. + */ + Tairon::Core::Signal1 bufferFullSignal; + + /** Emitted when there are new data in the buffer. + * + * The receiver gets pointer to this Reader as the first parameter and + * number of bytes read as the second one. + */ + Tairon::Core::Signal2 dataReadSignal; + + /** This signal is emitted when a socket error occurs. It could also + * mean that the connection has been closed. The socket is already + * closed when the signal is emitted. + */ + Tairon::Core::Signal2 errorSignal; + + protected: + /** Buffer for storing incoming data. + */ + char *buffer; + + /** Size of the buffer; + */ + unsigned int bufSize; + + /** Limiter used for limiting bandwidth. + */ + Limiter *limiter; + + /** Offset from the beginning of the buffer. + */ + unsigned int offset; + + /** Functor for calling read method. + */ + Tairon::Core::Functor0 *readFunctor; + + /** Socket from which the Reader is reading. + */ + Socket *socket; +}; + +}; // namespace Net + +}; // namespace Tairon + +#endif + +// vim: ai sw=4 ts=4 noet fdm=marker -- 2.11.4.GIT