moved library

This commit is contained in:
Zach Tibbitts
2007-01-07 22:50:11 +00:00
parent 514421b6f1
commit 99186a54c9
233 changed files with 68008 additions and 0 deletions

124
cpp/alert.cpp Executable file
View File

@@ -0,0 +1,124 @@
/*
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/alert.hpp"
namespace libtorrent {
alert::alert(severity_t severity, const std::string& msg)
: m_msg(msg)
, m_severity(severity)
, m_timestamp(boost::posix_time::second_clock::universal_time())
{
}
alert::~alert()
{
}
boost::posix_time::ptime alert::timestamp() const
{
return m_timestamp;
}
const std::string& alert::msg() const
{
return m_msg;
}
alert::severity_t alert::severity() const
{
return m_severity;
}
alert_manager::alert_manager()
: m_severity(alert::none)
{}
alert_manager::~alert_manager()
{
while (!m_alerts.empty())
{
delete m_alerts.front();
m_alerts.pop();
}
}
void alert_manager::post_alert(const alert& alert_)
{
boost::mutex::scoped_lock lock(m_mutex);
if (m_severity > alert_.severity()) return;
// the internal limit is 100 alerts
if (m_alerts.size() == 100)
{
alert* result = m_alerts.front();
m_alerts.pop();
delete result;
}
m_alerts.push(alert_.clone().release());
}
std::auto_ptr<alert> alert_manager::get()
{
boost::mutex::scoped_lock lock(m_mutex);
assert(!m_alerts.empty());
alert* result = m_alerts.front();
m_alerts.pop();
return std::auto_ptr<alert>(result);
}
bool alert_manager::pending() const
{
boost::mutex::scoped_lock lock(m_mutex);
return !m_alerts.empty();
}
void alert_manager::set_severity(alert::severity_t severity)
{
boost::mutex::scoped_lock lock(m_mutex);
m_severity = severity;
}
bool alert_manager::should_post(alert::severity_t severity) const
{
return severity >= m_severity;
}
} // namespace libtorrent

201
cpp/allocate_resources.cpp Normal file
View File

@@ -0,0 +1,201 @@
/*
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
//The Standard Library defines the two template functions std::min()
//and std::max() in the <algorithm> header. In general, you should
//use these template functions for calculating the min and max values
//of a pair. Unfortunately, Visual C++ does not define these function
// templates. This is because the names min and max clash with
//the traditional min and max macros defined in <windows.h>.
//As a workaround, Visual C++ defines two alternative templates with
//identical functionality called _cpp_min() and _cpp_max(). You can
//use them instead of std::min() and std::max().To disable the
//generation of the min and max macros in Visual C++, #define
//NOMINMAX before #including <windows.h>.
#ifdef _WIN32
//support boost1.32.0(2004-11-19 18:47)
//now all libs can be compiled and linked with static module
#define NOMINMAX
#endif
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/aux_/allocate_resources_impl.hpp"
#include <cassert>
#include <algorithm>
#include <boost/limits.hpp>
#if defined(_MSC_VER) && _MSC_VER < 1310
#define for if (false) {} else for
#else
#include <boost/iterator/transform_iterator.hpp>
#endif
namespace libtorrent
{
int saturated_add(int a, int b)
{
assert(a >= 0);
assert(b >= 0);
assert(a <= resource_request::inf);
assert(b <= resource_request::inf);
assert(resource_request::inf + resource_request::inf < 0);
unsigned int sum = unsigned(a) + unsigned(b);
if (sum > unsigned(resource_request::inf))
sum = resource_request::inf;
assert(sum >= unsigned(a) && sum >= unsigned(b));
return int(sum);
}
#if defined(_MSC_VER) && _MSC_VER < 1310
namespace detail
{
struct iterator_wrapper
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
orig_iter iter;
iterator_wrapper(orig_iter i): iter(i) {}
void operator++() { ++iter; }
torrent& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper& i) const
{ return iter != i.iter; }
};
struct iterator_wrapper2
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
orig_iter iter;
iterator_wrapper2(orig_iter i): iter(i) {}
void operator++() { ++iter; }
peer_connection& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper2& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper2& i) const
{ return iter != i.iter; }
};
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper(c.begin())
, detail::iterator_wrapper(c.end())
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper2(c.begin())
, detail::iterator_wrapper2(c.end())
, res);
}
#else
namespace aux
{
peer_connection& pick_peer(
std::pair<boost::shared_ptr<stream_socket>
, boost::intrusive_ptr<peer_connection> > const& p)
{
return *p.second;
}
peer_connection& pick_peer2(
std::pair<tcp::endpoint, peer_connection*> const& p)
{
return *p.second;
}
torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
{
return *p.second;
}
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::deref)
, new_iter(c.end(), &aux::deref)
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::pick_peer2)
, new_iter(c.end(), &aux::pick_peer2)
, res);
}
#endif
} // namespace libtorrent

1571
cpp/bt_peer_connection.cpp Executable file

File diff suppressed because it is too large Load Diff

344
cpp/entry.cpp Executable file
View File

@@ -0,0 +1,344 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <iomanip>
#include "libtorrent/entry.hpp"
#include "libtorrent/config.hpp"
#include <boost/bind.hpp>
#include <boost/next_prior.hpp>
#if defined(_MSC_VER)
namespace std
{
using ::isprint;
}
#define for if (false) {} else for
#endif
namespace
{
template <class T>
void call_destructor(T* o)
{
assert(o);
o->~T();
}
struct compare_string
{
compare_string(char const* s): m_str(s) {}
bool operator()(
std::pair<std::string
, libtorrent::entry> const& e) const
{
return m_str && e.first == m_str;
}
char const* m_str;
};
}
namespace libtorrent
{
namespace detail
{
TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val)
{
int sign = 0;
if (val < 0)
{
sign = 1;
val = -val;
}
buf[--size] = '\0';
if (val == 0) buf[--size] = '0';
for (; size > sign && val != 0;)
{
buf[--size] = '0' + char(val % 10);
val /= 10;
}
if (sign) buf[--size] = '-';
return buf + size;
}
}
entry& entry::operator[](char const* key)
{
dictionary_type::iterator i = dict().find(key);
if (i != dict().end()) return i->second;
dictionary_type::iterator ret = dict().insert(
dict().begin()
, std::make_pair(std::string(key), entry()));
return ret->second;
}
entry& entry::operator[](std::string const& key)
{
return (*this)[key.c_str()];
}
entry* entry::find_key(char const* key)
{
dictionary_type::iterator i = std::find_if(
dict().begin()
, dict().end()
, compare_string(key));
if (i == dict().end()) return 0;
return &i->second;
}
entry const* entry::find_key(char const* key) const
{
dictionary_type::const_iterator i = dict().find(key);
if (i == dict().end()) return 0;
return &i->second;
}
const entry& entry::operator[](char const* key) const
{
dictionary_type::const_iterator i = dict().find(key);
if (i == dict().end()) throw type_error(
(std::string("key not found: ") + key).c_str());
return i->second;
}
const entry& entry::operator[](std::string const& key) const
{
return (*this)[key.c_str()];
}
entry::entry(const dictionary_type& v)
{
new(data) dictionary_type(v);
m_type = dictionary_t;
}
entry::entry(const string_type& v)
{
new(data) string_type(v);
m_type = string_t;
}
entry::entry(const list_type& v)
{
new(data) list_type(v);
m_type = list_t;
}
entry::entry(const integer_type& v)
{
new(data) integer_type(v);
m_type = int_t;
}
void entry::operator=(const dictionary_type& v)
{
destruct();
new(data) dictionary_type(v);
m_type = dictionary_t;
}
void entry::operator=(const string_type& v)
{
destruct();
new(data) string_type(v);
m_type = string_t;
}
void entry::operator=(const list_type& v)
{
destruct();
new(data) list_type(v);
m_type = list_t;
}
void entry::operator=(const integer_type& v)
{
destruct();
new(data) integer_type(v);
m_type = int_t;
}
bool entry::operator==(entry const& e) const
{
if (m_type != e.m_type) return false;
switch(m_type)
{
case int_t:
return integer() == e.integer();
case string_t:
return string() == e.string();
case list_t:
return list() == e.list();
case dictionary_t:
return dict() == e.dict();
default:
assert(m_type == undefined_t);
return true;
}
}
void entry::construct(data_type t)
{
m_type = t;
switch(m_type)
{
case int_t:
new(data) integer_type;
break;
case string_t:
new(data) string_type;
break;
case list_t:
new(data) list_type;
break;
case dictionary_t:
new (data) dictionary_type;
break;
default:
assert(m_type == undefined_t);
m_type = undefined_t;
}
}
void entry::copy(const entry& e)
{
m_type = e.m_type;
switch(m_type)
{
case int_t:
new(data) integer_type(e.integer());
break;
case string_t:
new(data) string_type(e.string());
break;
case list_t:
new(data) list_type(e.list());
break;
case dictionary_t:
new (data) dictionary_type(e.dict());
break;
default:
m_type = undefined_t;
}
}
void entry::destruct()
{
switch(m_type)
{
case int_t:
call_destructor(reinterpret_cast<integer_type*>(data));
break;
case string_t:
call_destructor(reinterpret_cast<string_type*>(data));
break;
case list_t:
call_destructor(reinterpret_cast<list_type*>(data));
break;
case dictionary_t:
call_destructor(reinterpret_cast<dictionary_type*>(data));
break;
default:
assert(m_type == undefined_t);
break;
}
}
void entry::print(std::ostream& os, int indent) const
{
assert(indent >= 0);
for (int i = 0; i < indent; ++i) os << " ";
switch (m_type)
{
case int_t:
os << integer() << "\n";
break;
case string_t:
{
bool binary_string = false;
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
{
if (!std::isprint(static_cast<unsigned char>(*i)))
{
binary_string = true;
break;
}
}
if (binary_string)
{
os.unsetf(std::ios_base::dec);
os.setf(std::ios_base::hex);
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
os << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>((unsigned char)*i);
os.unsetf(std::ios_base::hex);
os.setf(std::ios_base::dec);
os << "\n";
}
else
{
os << string() << "\n";
}
} break;
case list_t:
{
os << "list\n";
for (list_type::const_iterator i = list().begin(); i != list().end(); ++i)
{
i->print(os, indent+1);
}
} break;
case dictionary_t:
{
os << "dictionary\n";
for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i)
{
for (int j = 0; j < indent+1; ++j) os << " ";
os << "[" << i->first << "]";
if (i->second.type() != entry::string_t
&& i->second.type() != entry::int_t)
os << "\n";
else os << " ";
i->second.print(os, indent+2);
}
} break;
default:
os << "<uninitialized>\n";
}
}
}

148
cpp/escape_string.cpp Executable file
View File

@@ -0,0 +1,148 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <cassert>
#include <stdexcept>
#include <sstream>
#include <iomanip>
#include <cctype>
#include <algorithm>
namespace libtorrent
{
std::string unescape_string(std::string const& s)
{
std::string ret;
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
{
if(*i == '+')
{
ret += ' ';
}
else if (*i != '%')
{
ret += *i;
}
else
{
++i;
if (i == s.end())
throw std::runtime_error("invalid escaped string");
int high;
if(*i >= '0' && *i <= '9') high = *i - '0';
else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
++i;
if (i == s.end())
throw std::runtime_error("invalid escaped string");
int low;
if(*i >= '0' && *i <= '9') low = *i - '0';
else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
ret += char(high * 16 + low);
}
}
return ret;
}
std::string escape_string(const char* str, int len)
{
assert(str != 0);
assert(len >= 0);
// http://www.ietf.org/rfc/rfc2396.txt
// section 2.3
// some trackers seems to require that ' is escaped
// static const char unreserved_chars[] = "-_.!~*'()";
static const char unreserved_chars[] = "-_.!~*()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789";
std::stringstream ret;
ret << std::hex << std::setfill('0');
for (int i = 0; i < len; ++i)
{
if (std::count(
unreserved_chars
, unreserved_chars+sizeof(unreserved_chars)-1
, *str))
{
ret << *str;
}
else
{
ret << '%'
<< std::setw(2)
<< (int)static_cast<unsigned char>(*str);
}
++str;
}
return ret.str();
}
std::string escape_path(const char* str, int len)
{
assert(str != 0);
assert(len >= 0);
static const char unreserved_chars[] = "/-_.!~*()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789";
std::stringstream ret;
ret << std::hex << std::setfill('0');
for (int i = 0; i < len; ++i)
{
if (std::count(
unreserved_chars
, unreserved_chars+sizeof(unreserved_chars)-1
, *str))
{
ret << *str;
}
else
{
ret << '%'
<< std::setw(2)
<< (int)static_cast<unsigned char>(*str);
}
++str;
}
return ret.str();
}
}

313
cpp/file.cpp Executable file
View File

@@ -0,0 +1,313 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _WIN32
// windows part
#include "libtorrent/utf8.hpp"
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _MODE_T_
typedef int mode_t;
#endif
#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif
#else
// unix part
#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <boost/static_assert.hpp>
// make sure the _FILE_OFFSET_BITS define worked
// on this platform
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
#endif
#include <boost/filesystem/operations.hpp>
#include "libtorrent/file.hpp"
#include <sstream>
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_RANDOM
#define O_RANDOM 0
#endif
#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif
namespace fs = boost::filesystem;
namespace
{
enum { mode_in = 1, mode_out = 2 };
mode_t map_open_mode(int m)
{
if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
assert(false);
return 0;
}
#ifdef WIN32
std::string utf8_native(std::string const& s)
{
try
{
std::wstring ws;
libtorrent::utf8_wchar(s, ws);
std::size_t size = wcstombs(0, ws.c_str(), 0);
if (size == std::size_t(-1)) return s;
std::string ret;
ret.resize(size);
size = wcstombs(&ret[0], ws.c_str(), size + 1);
if (size == wchar_t(-1)) return s;
ret.resize(size);
return ret;
}
catch(std::exception)
{
return s;
}
}
#else
std::string utf8_native(std::string const& s)
{
return s;
}
#endif
}
namespace libtorrent
{
const file::open_mode file::in(mode_in);
const file::open_mode file::out(mode_out);
const file::seek_mode file::begin(1);
const file::seek_mode file::end(2);
struct file::impl
{
impl()
: m_fd(-1)
, m_open_mode(0)
{}
impl(fs::path const& path, int mode)
: m_fd(-1)
, m_open_mode(0)
{
open(path, mode);
}
~impl()
{
close();
}
void open(fs::path const& path, int mode)
{
assert(path.is_complete());
close();
#if defined(_WIN32) && defined(UNICODE)
std::wstring wpath(safe_convert(path.native_file_string()));
m_fd = ::_wopen(
wpath.c_str()
, map_open_mode(mode)
, S_IREAD | S_IWRITE);
#else
m_fd = ::open(
utf8_native(path.native_file_string()).c_str()
, map_open_mode(mode)
#ifdef _WIN32
, S_IREAD | S_IWRITE);
#else
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
#endif
if (m_fd == -1)
{
std::stringstream msg;
msg << "open failed: '" << path.native_file_string() << "'. "
<< strerror(errno);
throw file_error(msg.str());
}
m_open_mode = mode;
}
void close()
{
if (m_fd == -1) return;
::close(m_fd);
m_fd = -1;
m_open_mode = 0;
}
size_type read(char* buf, size_type num_bytes)
{
assert(m_open_mode & mode_in);
assert(m_fd != -1);
size_type ret = ::read(m_fd, buf, num_bytes);
if (ret == -1)
{
std::stringstream msg;
msg << "read failed: " << strerror(errno);
throw file_error(msg.str());
}
return ret;
}
size_type write(const char* buf, size_type num_bytes)
{
assert(m_open_mode & mode_out);
assert(m_fd != -1);
// TODO: Test this a bit more, what happens with random failures in
// the files?
// if ((rand() % 100) > 80)
// throw file_error("debug");
size_type ret = ::write(m_fd, buf, num_bytes);
if (ret == -1)
{
std::stringstream msg;
msg << "write failed: " << strerror(errno);
throw file_error(msg.str());
}
return ret;
}
size_type seek(size_type offset, int m)
{
assert(m_open_mode);
assert(m_fd != -1);
int seekdir = (m == 1)?SEEK_SET:SEEK_END;
#ifdef _WIN32
size_type ret = _lseeki64(m_fd, offset, seekdir);
#else
size_type ret = lseek(m_fd, offset, seekdir);
#endif
// For some strange reason this fails
// on win32. Use windows specific file
// wrapper instead.
if (ret == -1)
{
std::stringstream msg;
msg << "seek failed: '" << strerror(errno)
<< "' fd: " << m_fd
<< " offset: " << offset
<< " seekdir: " << seekdir;
throw file_error(msg.str());
}
return ret;
}
size_type tell()
{
assert(m_open_mode);
assert(m_fd != -1);
#ifdef _WIN32
return _telli64(m_fd);
#else
return lseek(m_fd, 0, SEEK_CUR);
#endif
}
int m_fd;
int m_open_mode;
};
// pimpl forwardings
file::file() : m_impl(new impl()) {}
file::file(boost::filesystem::path const& p, file::open_mode m)
: m_impl(new impl(p, m.m_mask))
{}
file::~file() {}
void file::open(boost::filesystem::path const& p, file::open_mode m)
{
m_impl->open(p, m.m_mask);
}
void file::close()
{
m_impl->close();
}
size_type file::write(const char* buf, size_type num_bytes)
{
return m_impl->write(buf, num_bytes);
}
size_type file::read(char* buf, size_type num_bytes)
{
return m_impl->read(buf, num_bytes);
}
size_type file::seek(size_type pos, file::seek_mode m)
{
return m_impl->seek(pos, m.m_val);
}
size_type file::tell()
{
return m_impl->tell();
}
}

1211
cpp/flood_core.cpp Executable file

File diff suppressed because it is too large Load Diff

845
cpp/http_tracker_connection.cpp Executable file
View File

@@ -0,0 +1,845 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/io.hpp"
using namespace libtorrent;
using boost::bind;
namespace
{
enum
{
minimum_tracker_response_length = 3,
http_buffer_size = 2048
};
enum
{
FTEXT = 0x01,
FHCRC = 0x02,
FEXTRA = 0x04,
FNAME = 0x08,
FCOMMENT = 0x10,
FRESERVED = 0xe0,
GZIP_MAGIC0 = 0x1f,
GZIP_MAGIC1 = 0x8b
};
}
using namespace boost::posix_time;
namespace libtorrent
{
http_parser::http_parser()
: m_recv_pos(0)
, m_status_code(-1)
, m_content_length(-1)
, m_content_encoding(plain)
, m_state(read_status)
, m_recv_buffer(0, 0)
, m_body_start_pos(0)
, m_finished(false)
{}
boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
{
m_recv_buffer = recv_buffer;
boost::tuple<int, int> ret(0, 0);
char const* pos = recv_buffer.begin + m_recv_pos;
if (m_state == read_status)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
// if we don't have a full line yet, wait.
if (newline == recv_buffer.end) return ret;
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
std::istringstream line(std::string(pos, newline - 1));
++newline;
int incoming = (int)std::distance(pos, newline);
m_recv_pos += incoming;
boost::get<1>(ret) += incoming;
pos = newline;
line >> m_protocol;
if (m_protocol.substr(0, 5) != "HTTP/")
{
throw std::runtime_error("unknown protocol in HTTP response: "
+ m_protocol);
}
line >> m_status_code;
std::getline(line, m_server_message);
m_state = read_header;
}
if (m_state == read_header)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
std::string line;
while (newline != recv_buffer.end && m_state == read_header)
{
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
line.assign(pos, newline - 1);
m_recv_pos += newline - pos;
boost::get<1>(ret) += newline - pos;
pos = newline;
std::string::size_type separator = line.find(": ");
if (separator == std::string::npos)
{
++pos;
++m_recv_pos;
boost::get<1>(ret) += 1;
m_state = read_body;
m_body_start_pos = m_recv_pos;
break;
}
std::string name = line.substr(0, separator);
std::string value = line.substr(separator + 2, std::string::npos);
m_header.insert(std::make_pair(name, value));
if (name == "Content-Length")
{
try
{
m_content_length = boost::lexical_cast<int>(value);
}
catch(boost::bad_lexical_cast&) {}
}
else if (name == "Content-Encoding")
{
if (value == "gzip" || value == "x-gzip")
{
m_content_encoding = gzip;
}
else
{
std::string error_str = "unknown content encoding in response: \"";
error_str += value;
error_str += "\"";
throw std::runtime_error(error_str);
}
}
// TODO: make sure we don't step outside of the buffer
++pos;
++m_recv_pos;
assert(m_recv_pos <= (int)recv_buffer.left());
newline = std::find(pos, recv_buffer.end, '\n');
}
}
if (m_state == read_body)
{
int incoming = recv_buffer.end - pos;
if (m_recv_pos - m_body_start_pos + incoming > m_content_length
&& m_content_length >= 0)
incoming = m_content_length - m_recv_pos + m_body_start_pos;
assert(incoming >= 0);
m_recv_pos += incoming;
boost::get<0>(ret) += incoming;
if (m_content_length >= 0
&& m_recv_pos - m_body_start_pos >= m_content_length)
{
m_finished = true;
}
}
return ret;
}
buffer::const_interval http_parser::get_body()
{
char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
char const* body_end = m_recv_buffer.begin + m_recv_pos;
m_recv_pos = 0;
m_body_start_pos = 0;
m_status_code = -1;
m_content_length = -1;
m_finished = false;
m_state = read_status;
m_header.clear();
return buffer::const_interval(body_begin, body_end);
}
http_tracker_connection::http_tracker_connection(
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, std::string request
, boost::weak_ptr<request_callback> c
, session_settings const& stn
, std::string const& auth)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_state(read_status)
, m_content_encoding(plain)
, m_content_length(0)
, m_name_lookup(d)
, m_port(port)
, m_recv_pos(0)
, m_buffer(http_buffer_size)
, m_settings(stn)
, m_password(auth)
, m_code(0)
, m_timed_out(false)
{
const std::string* connect_to_host;
bool using_proxy = false;
m_send_buffer.assign("GET ");
// should we use the proxy?
if (!m_settings.proxy_ip.empty())
{
connect_to_host = &m_settings.proxy_ip;
using_proxy = true;
m_send_buffer += "http://";
m_send_buffer += hostname;
if (port != 80)
{
m_send_buffer += ":";
m_send_buffer += boost::lexical_cast<std::string>(port);
}
m_port = m_settings.proxy_port != 0
? m_settings.proxy_port : 80 ;
}
else
{
connect_to_host = &hostname;
}
if (tracker_req().kind == tracker_request::scrape_request)
{
// find and replace "announce" with "scrape"
// in request
std::size_t pos = request.find("announce");
if (pos == std::string::npos)
throw std::runtime_error("scrape is not available on url: '"
+ tracker_req().url +"'");
request.replace(pos, 8, "scrape");
}
m_send_buffer += request;
// if request-string already contains
// some parameters, append an ampersand instead
// of a question mark
if (request.find('?') != std::string::npos)
m_send_buffer += "&";
else
m_send_buffer += "?";
m_send_buffer += "info_hash=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
if (tracker_req().kind == tracker_request::announce_request)
{
m_send_buffer += "&peer_id=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.pid.begin()), 20);
m_send_buffer += "&port=";
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
m_send_buffer += "&uploaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
m_send_buffer += "&downloaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
m_send_buffer += "&left=";
m_send_buffer += boost::lexical_cast<std::string>(req.left);
if (req.event != tracker_request::none)
{
const char* event_string[] = {"completed", "started", "stopped"};
m_send_buffer += "&event=";
m_send_buffer += event_string[req.event - 1];
}
m_send_buffer += "&key=";
std::stringstream key_string;
key_string << std::hex << req.key;
m_send_buffer += key_string.str();
m_send_buffer += "&compact=1";
m_send_buffer += "&numwant=";
m_send_buffer += boost::lexical_cast<std::string>(
std::min(req.num_want, 999));
// extension that tells the tracker that
// we don't need any peer_id's in the response
m_send_buffer += "&no_peer_id=1";
}
m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
"User-Agent: ";
m_send_buffer += m_settings.user_agent;
m_send_buffer += "\r\n"
"Host: ";
m_send_buffer += hostname;
if (port != 80)
{
m_send_buffer += ':';
m_send_buffer += boost::lexical_cast<std::string>(port);
}
if (using_proxy && !m_settings.proxy_login.empty())
{
m_send_buffer += "\r\nProxy-Authorization: Basic ";
m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password);
}
if (auth != "")
{
m_send_buffer += "\r\nAuthorization: Basic ";
m_send_buffer += base64encode(auth);
}
m_send_buffer += "\r\n\r\n";
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
std::stringstream info_hash_str;
info_hash_str << req.info_hash;
requester().debug_log("info_hash: " + info_hash_str.str() + "\n");
}
#endif
tcp::resolver::query q(*connect_to_host, "0");
m_name_lookup.async_resolve(q
, boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
void http_tracker_connection::on_timeout()
{
m_timed_out = true;
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void http_tracker_connection::name_lookup(asio::error const& error
, tcp::resolver::iterator i) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error || i == tcp::resolver::iterator())
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker name lookup successful");
#endif
restart_read_timeout();
m_socket.reset(new stream_socket(m_name_lookup.io_service()));
tcp::endpoint a(i->endpoint().address(), m_port);
if (has_requester()) requester().m_tracker_address = a;
m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
};
void http_tracker_connection::connected(asio::error const& error) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection successful");
#endif
restart_read_timeout();
async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
, m_send_buffer.size()), bind(&http_tracker_connection::sent
, self(), _1));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}
void http_tracker_connection::sent(asio::error const& error) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker send data completed");
#endif
restart_read_timeout();
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}; // msvc 7.1 seems to require this semi-colon
void http_tracker_connection::receive(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
if (error == asio::error::eof)
{
on_response();
close();
return;
}
fail(-1, error.what());
return;
}
restart_read_timeout();
assert(bytes_transferred > 0);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection reading "
+ boost::lexical_cast<std::string>(bytes_transferred));
#endif
m_recv_pos += bytes_transferred;
// if the receive buffer is full, expand it with http_buffer_size
if ((int)m_buffer.size() == m_recv_pos)
{
if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
{
fail(200, "too large tracker response");
return;
}
assert(http_buffer_size > 0);
if ((int)m_buffer.size() + http_buffer_size
> m_settings.tracker_maximum_response_length)
m_buffer.resize(m_settings.tracker_maximum_response_length);
else
m_buffer.resize(m_buffer.size() + http_buffer_size);
}
if (m_state == read_status)
{
std::vector<char>::iterator end = m_buffer.begin()+m_recv_pos;
std::vector<char>::iterator newline = std::find(m_buffer.begin(), end, '\n');
// if we don't have a full line yet, wait.
if (newline != end)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline));
#endif
std::istringstream line(std::string(m_buffer.begin(), newline));
++newline;
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
m_buffer.erase(m_buffer.begin(), newline);
std::string protocol;
line >> m_server_protocol;
if (m_server_protocol.substr(0, 5) != "HTTP/")
{
std::string error_msg = "unknown protocol in response: " + m_server_protocol;
fail(-1, error_msg.c_str());
return;
}
line >> m_code;
std::getline(line, m_server_message);
m_state = read_header;
}
}
if (m_state == read_header)
{
std::vector<char>::iterator end = m_buffer.begin() + m_recv_pos;
std::vector<char>::iterator newline
= std::find(m_buffer.begin(), end, '\n');
std::string line;
while (newline != end && m_state == read_header)
{
line.assign(m_buffer.begin(), newline);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log(line);
#endif
if (line.substr(0, 16) == "Content-Length: ")
{
try
{
m_content_length = boost::lexical_cast<int>(
line.substr(16, line.length() - 17));
}
catch(boost::bad_lexical_cast&)
{
fail(-1, "invalid content-length in tracker response");
return;
}
if (m_content_length > m_settings.tracker_maximum_response_length)
{
fail(-1, "content-length is greater than maximum response length");
return;
}
if (m_content_length < minimum_tracker_response_length && m_code == 200)
{
fail(-1, "content-length is smaller than minimum response length");
return;
}
}
else if (line.substr(0, 18) == "Content-Encoding: ")
{
if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip")
{
m_content_encoding = gzip;
}
else
{
std::string error_str = "unknown content encoding in response: \"";
error_str += line.substr(18, line.length() - 18 - 2);
error_str += "\"";
fail(-1, error_str.c_str());
return;
}
}
else if (line.substr(0, 10) == "Location: ")
{
m_location.assign(line.begin() + 10, line.end());
}
else if (line.substr(0, 7) == "Server: ")
{
m_server.assign(line.begin() + 7, line.end());
}
else if (line.size() < 3)
{
m_state = read_body;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("end of http header");
#endif
if (m_code >= 300 && m_code < 400)
{
if (m_location.empty())
{
std::string error_str = "got redirection response (";
error_str += boost::lexical_cast<std::string>(m_code);
error_str += ") without 'Location' header";
fail(-1, error_str.c_str());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\"");
#endif
tracker_request req = tracker_req();
std::string::size_type i = m_location.find('?');
if (i == std::string::npos)
req.url = m_location;
else
req.url.assign(m_location.begin(), m_location.begin() + i);
m_man.queue_request(m_socket->io_service(), req
, m_password, m_requester);
close();
return;
}
}
++newline;
assert(m_recv_pos <= (int)m_buffer.size());
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
m_buffer.erase(m_buffer.begin(), newline);
assert(m_recv_pos <= (int)m_buffer.size());
end = m_buffer.begin() + m_recv_pos;
newline = std::find(m_buffer.begin(), end, '\n');
}
}
if (m_state == read_body)
{
if (m_recv_pos == m_content_length)
{
on_response();
close();
return;
}
}
else if (m_recv_pos > m_content_length && m_content_length > 0)
{
fail(-1, "invalid tracker response (body > content_length)");
return;
}
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
};
void http_tracker_connection::on_response()
{
// GZIP
if (m_content_encoding == gzip)
{
boost::shared_ptr<request_callback> r = m_requester.lock();
if (!r)
{
close();
return;
}
if (inflate_gzip(m_buffer, tracker_request(), r.get(),
m_settings.tracker_maximum_response_length))
{
close();
return;
}
}
// handle tracker response
try
{
entry e = bdecode(m_buffer.begin(), m_buffer.end());
parse(e);
}
catch (std::exception& e)
{
std::string error_str(e.what());
error_str += ": ";
error_str.append(m_buffer.begin(), m_buffer.end());
fail(m_code, error_str.c_str());
}
#ifndef NDEBUG
catch (...)
{
assert(false);
}
#endif
}
peer_entry http_tracker_connection::extract_peer_info(const entry& info)
{
peer_entry ret;
// extract peer id (if any)
entry const* i = info.find_key("peer id");
if (i != 0)
{
if (i->string().length() != 20)
throw std::runtime_error("invalid response from tracker");
std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
}
else
{
// if there's no peer_id, just initialize it to a bunch of zeroes
std::fill_n(ret.pid.begin(), 20, 0);
}
// extract ip
i = info.find_key("ip");
if (i == 0) throw std::runtime_error("invalid response from tracker");
ret.ip = i->string();
// extract port
i = info.find_key("port");
if (i == 0) throw std::runtime_error("invalid response from tracker");
ret.port = (unsigned short)i->integer();
return ret;
}
void http_tracker_connection::parse(entry const& e)
{
if (!has_requester()) return;
try
{
// parse the response
try
{
entry const& failure = e["failure reason"];
fail(m_code, failure.string().c_str());
return;
}
catch (type_error const&) {}
try
{
entry const& warning = e["warning message"];
if (has_requester())
requester().tracker_warning(warning.string());
}
catch(type_error const&) {}
std::vector<peer_entry> peer_list;
if (tracker_req().kind == tracker_request::scrape_request)
{
std::string ih;
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
, std::back_inserter(ih));
entry scrape_data = e["files"][ih];
int complete = scrape_data["complete"].integer();
int incomplete = scrape_data["incomplete"].integer();
requester().tracker_response(tracker_request(), peer_list, 0, complete
, incomplete);
return;
}
int interval = (int)e["interval"].integer();
if (e["peers"].type() == entry::string_t)
{
std::string const& peers = e["peers"].string();
for (std::string::const_iterator i = peers.begin();
i != peers.end();)
{
if (std::distance(i, peers.end()) < 6) break;
peer_entry p;
p.pid.clear();
std::stringstream ip_str;
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i);
p.ip = ip_str.str();
p.port = detail::read_uint16(i);
peer_list.push_back(p);
}
}
else
{
entry::list_type const& l = e["peers"].list();
for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
{
peer_entry p = extract_peer_info(*i);
peer_list.push_back(p);
}
}
// look for optional scrape info
int complete = -1;
int incomplete = -1;
try { complete = e["complete"].integer(); }
catch(type_error&) {}
try { incomplete = e["incomplete"].integer(); }
catch(type_error&) {}
requester().tracker_response(tracker_request(), peer_list, interval, complete
, incomplete);
}
catch(type_error& e)
{
requester().tracker_request_error(tracker_request(), m_code, e.what());
}
catch(std::runtime_error& e)
{
requester().tracker_request_error(tracker_request(), m_code, e.what());
}
}
}

326
cpp/identify_client.cpp Executable file
View File

@@ -0,0 +1,326 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/optional.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/identify_client.hpp"
#include "libtorrent/fingerprint.hpp"
namespace
{
using namespace libtorrent;
int decode_digit(char c)
{
if (std::isdigit(c)) return c - '0';
return unsigned(c) - 'A' + 10;
}
// takes a peer id and returns a valid boost::optional
// object if the peer id matched the azureus style encoding
// the returned fingerprint contains information about the
// client's id
boost::optional<fingerprint> parse_az_style(const peer_id& id)
{
fingerprint ret("..", 0, 0, 0, 0);
if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
|| (id[3] < '0') || (id[4] < '0')
|| (id[5] < '0') || (id[6] < '0')
|| id[7] != '-')
return boost::optional<fingerprint>();
ret.name[0] = id[1];
ret.name[1] = id[2];
ret.major_version = decode_digit(id[3]);
ret.minor_version = decode_digit(id[4]);
ret.revision_version = decode_digit(id[5]);
ret.tag_version = decode_digit(id[6]);
return boost::optional<fingerprint>(ret);
}
// checks if a peer id can possibly contain a shadow-style
// identification
boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
{
fingerprint ret("..", 0, 0, 0, 0);
if (!std::isalnum(id[0]))
return boost::optional<fingerprint>();
if (std::equal(id.begin()+4, id.begin()+6, "--"))
{
if ((id[1] < '0') || (id[2] < '0')
|| (id[3] < '0'))
return boost::optional<fingerprint>();
ret.major_version = decode_digit(id[1]);
ret.minor_version = decode_digit(id[2]);
ret.revision_version = decode_digit(id[3]);
}
else
{
if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
return boost::optional<fingerprint>();
ret.major_version = id[1];
ret.minor_version = id[2];
ret.revision_version = id[3];
}
ret.name[0] = id[0];
ret.name[1] = 0;
ret.tag_version = 0;
return boost::optional<fingerprint>(ret);
}
// checks if a peer id can possibly contain a mainline-style
// identification
boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
{
char ids[21];
std::copy(id.begin(), id.end(), ids);
ids[20] = 0;
fingerprint ret("..", 0, 0, 0, 0);
ret.name[1] = 0;
ret.tag_version = 0;
if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
, &ret.revision_version) != 4
|| !std::isprint(ret.name[0]))
return boost::optional<fingerprint>();
return boost::optional<fingerprint>(ret);
}
typedef std::pair<char const*, char const*> map_entry;
// only support BitTorrentSpecification
// must be ordered alphabetically
map_entry name_map[] =
{
map_entry("A", "ABC")
, map_entry("AR", "Arctic Torrent")
, map_entry("AX", "BitPump")
, map_entry("AZ", "Azureus")
, map_entry("BB", "BitBuddy")
, map_entry("BC", "BitComet")
, map_entry("BS", "BTSlave")
, map_entry("BX", "BittorrentX")
, map_entry("CD", "Enhanced CTorrent")
, map_entry("CT", "CTorrent")
, map_entry("DE", "Deluge")
, map_entry("ES", "electric sheep")
, map_entry("KT", "KTorrent")
, map_entry("LP", "lphant")
, map_entry("LT", "libtorrent")
, map_entry("M", "Mainline")
, map_entry("MP", "MooPolice")
, map_entry("MT", "Moonlight Torrent")
, map_entry("O", "Osprey Permaseed")
, map_entry("R", "Tribler")
, map_entry("S", "Shadow")
, map_entry("SB", "Swiftbit")
, map_entry("SN", "ShareNet")
, map_entry("SS", "SwarmScope")
, map_entry("SZ", "Shareaza")
, map_entry("T", "BitTornado")
, map_entry("TN", "Torrent.NET")
, map_entry("TR", "Transmission")
, map_entry("TS", "TorrentStorm")
, map_entry("U", "UPnP")
, map_entry("UL", "uLeecher")
, map_entry("UT", "MicroTorrent")
, map_entry("XT", "XanTorrent")
, map_entry("ZT", "ZipTorrent")
, map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
, map_entry("pX", "pHoeniX")
, map_entry("qB", "qBittorrent")
};
bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
{
return lhs.first[0] < rhs.first[0]
|| ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
}
std::string lookup(fingerprint const& f)
{
std::stringstream identity;
const int size = sizeof(name_map)/sizeof(name_map[0]);
map_entry* i =
std::lower_bound(name_map, name_map + size
, map_entry(f.name, ""), &compare_first_string);
#ifndef NDEBUG
for (int i = 1; i < size; ++i)
{
assert(compare_first_string(name_map[i-1]
, name_map[i]));
}
#endif
if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
identity << i->second;
else
{
identity << f.name[0];
if (f.name[1] != 0) identity << f.name[1];
}
identity << " " << (int)f.major_version
<< "." << (int)f.minor_version
<< "." << (int)f.revision_version;
if (f.name[1] != 0)
identity << "." << (int)f.tag_version;
return identity.str();
}
bool find_string(unsigned char const* id, char const* search)
{
return std::equal(search, search + std::strlen(search), id);
}
}
namespace libtorrent
{
boost::optional<fingerprint> client_fingerprint(peer_id const& p)
{
// look for azureus style id
boost::optional<fingerprint> f;
f = parse_az_style(p);
if (f) return f;
// look for shadow style id
f = parse_shadow_style(p);
if (f) return f;
// look for mainline style id
f = parse_mainline_style(p);
if (f) return f;
return f;
}
std::string identify_client(peer_id const& p)
{
peer_id::const_iterator PID = p.begin();
boost::optional<fingerprint> f;
if (p.is_all_zeros()) return "Unknown";
// ----------------------
// non standard encodings
// ----------------------
if (find_string(PID, "Deadman Walking-")) return "Deadman";
if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
if (find_string(PID, "DansClient")) return "XanTorrent";
if (find_string(PID + 4, "btfans")) return "SimpleBT";
if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
if (find_string(PID, "martini")) return "Martini Man";
if (find_string(PID, "Plus---")) return "Bittorrent Plus";
if (find_string(PID, "turbobt")) return "TurboBT";
if (find_string(PID, "a00---0")) return "Swarmy";
if (find_string(PID, "a02---0")) return "Swarmy";
if (find_string(PID, "T00---0")) return "Teeweety";
if (find_string(PID, "BTDWV-")) return "Deadman Walking";
if (find_string(PID + 2, "BS")) return "BitSpirit";
if (find_string(PID, "btuga")) return "BTugaXP";
if (find_string(PID, "oernu")) return "BTugaXP";
if (find_string(PID, "Mbrst")) return "Burst!";
if (find_string(PID, "Plus")) return "Plus!";
if (find_string(PID, "-Qt-")) return "Qt";
if (find_string(PID, "exbc")) return "BitComet";
if (find_string(PID, "-G3")) return "G3 Torrent";
if (find_string(PID, "XBT")) return "XBT";
if (find_string(PID, "OP")) return "Opera";
if (find_string(PID, "-BOW") && PID[7] == '-')
return "Bits on Wheels " + std::string(PID + 4, PID + 7);
if (find_string(PID, "eX"))
{
std::string user(PID + 2, PID + 14);
return std::string("eXeem ('") + user.c_str() + "')";
}
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
return "Experimental 3.2.1b2";
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
return "Experimental 3.1";
// look for azureus style id
f = parse_az_style(p);
if (f) return lookup(*f);
// look for shadow style id
f = parse_shadow_style(p);
if (f) return lookup(*f);
// look for mainline style id
f = parse_mainline_style(p);
if (f) return lookup(*f);
if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
return "Generic";
std::string unknown("Unknown [");
for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
{
unknown += std::isprint(*i)?*i:'.';
}
unknown += "]";
return unknown;
}
}

80
cpp/ip_filter.cpp Normal file
View File

@@ -0,0 +1,80 @@
/*
Copyright (c) 2005, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/ip_filter.hpp"
#include <boost/utility.hpp>
//#include <iostream>
namespace libtorrent
{
void ip_filter::add_rule(address first, address last, int flags)
{
if (first.is_v4())
{
assert(last.is_v4());
m_filter4.add_rule(first.to_v4(), last.to_v4(), flags);
}
else if (first.is_v6())
{
assert(last.is_v6());
m_filter6.add_rule(first.to_v6(), last.to_v6(), flags);
}
else
assert(false);
}
int ip_filter::access(address const& addr) const
{
if (addr.is_v4())
return m_filter4.access(addr.to_v4());
assert(addr.is_v6());
return m_filter6.access(addr.to_v6());
}
ip_filter::filter_tuple_t ip_filter::export_filter() const
{
return boost::make_tuple(m_filter4.export_filter()
, m_filter6.export_filter());
}
/*
void ip_filter::print() const
{
for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i)
{
std::cout << i->start.as_string() << " " << i->access << "\n";
}
}
*/
}

View File

@@ -0,0 +1,145 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/closest_nodes.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef boost::shared_ptr<observer> observer_ptr;
class closest_nodes_observer : public observer
{
public:
closest_nodes_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, node_id self
, node_id target
)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& p)
{
p.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<traversal_algorithm> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void closest_nodes_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void closest_nodes_observer::timeout()
{
m_algorithm->failed(m_self);
}
closest_nodes::closest_nodes(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
{
boost::intrusive_ptr<closest_nodes> self(this);
add_requests();
}
void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
{
observer_ptr p(new closest_nodes_observer(this, id, m_target));
m_rpc.invoke(messages::find_node, addr, p);
}
void closest_nodes::done()
{
std::vector<node_entry> results;
int result_size = m_table.bucket_size();
if (result_size > (int)m_results.size()) result_size = (int)m_results.size();
for (std::vector<result>::iterator i = m_results.begin()
, end(m_results.begin() + result_size); i != end; ++i)
{
results.push_back(node_entry(i->id, i->addr));
}
m_done_callback(results);
}
void closest_nodes::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

View File

@@ -0,0 +1,905 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <fstream>
#include <set>
#include <numeric>
#include <stdexcept>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/operations.hpp>
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/traversal_algorithm.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/version.hpp"
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::second_clock;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::hours;
using boost::posix_time::milliseconds;
using boost::ref;
using boost::lexical_cast;
using libtorrent::dht::node_impl;
using libtorrent::dht::node_id;
using libtorrent::dht::packet_t;
using libtorrent::dht::msg;
using libtorrent::dht::packet_iterator;
namespace messages = libtorrent::dht::messages;
using namespace libtorrent::detail;
using asio::ip::udp;
typedef asio::ip::address_v4 address;
namespace
{
const int tick_period = 1; // minutes
struct count_peers
{
int& count;
count_peers(int& c): count(c) {}
void operator()(std::pair<libtorrent::dht::node_id
, libtorrent::dht::torrent_entry> const& t)
{
count += std::distance(t.second.peers.begin()
, t.second.peers.end());
}
};
boost::optional<node_id> read_id(libtorrent::entry const& d)
{
using namespace libtorrent;
using libtorrent::dht::node_id;
if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
entry const* nid = d.find_key("node-id");
if (!nid
|| nid->type() != entry::string_t
|| nid->string().length() != 40)
return boost::optional<node_id>();
return boost::optional<node_id>(
boost::lexical_cast<node_id>(nid->string()));
}
template <class EndpointType>
void read_endpoint_list(libtorrent::entry const* n, std::vector<EndpointType>& epl)
{
using namespace libtorrent;
entry::list_type const& contacts = n->list();
for (entry::list_type::const_iterator i = contacts.begin()
, end(contacts.end()); i != end; ++i)
{
std::string const& p = i->string();
if (p.size() < 6) continue;
std::string::const_iterator in = p.begin();
if (p.size() == 6)
epl.push_back(read_v4_endpoint<EndpointType>(in));
else if (p.size() == 18)
epl.push_back(read_v6_endpoint<EndpointType>(in));
}
}
}
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(dht_tracker)
#endif
// class that puts the networking and the kademlia node in a single
// unit and connecting them together.
dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings
, asio::ip::address listen_interface, entry const& bootstrap)
: m_demuxer(d)
, m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port))
, m_dht(bind(&dht_tracker::send_packet, this, _1), settings
, read_id(bootstrap))
, m_buffer(0)
, m_last_refresh(second_clock::universal_time() - hours(1))
, m_timer(m_demuxer)
, m_connection_timer(m_demuxer)
, m_refresh_timer(m_demuxer)
, m_settings(settings)
, m_refresh_bucket(160)
, m_host_resolver(d)
{
using boost::bind;
m_in_buf[0].resize(1000);
m_in_buf[1].resize(1000);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
m_counter = 0;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
m_mp_message_input = 0;
m_gr_message_input = 0;
m_total_in_bytes = 0;
m_total_out_bytes = 0;
m_queries_out_bytes = 0;
// turns on and off individual components' logging
// rpc_log().enable(false);
// node_log().enable(false);
// traversal_log().enable(false);
// dht_tracker_log.enable(false);
#endif
std::vector<udp::endpoint> initial_nodes;
if (bootstrap.type() == entry::dictionary_t)
{
try
{
if (entry const* nodes = bootstrap.find_key("nodes"))
read_endpoint_list<udp::endpoint>(nodes, initial_nodes);
} catch (std::exception&) {}
}
m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this));
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
m_timer.expires_from_now(seconds(1));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_connection_timer.expires_from_now(seconds(10));
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
m_refresh_timer.expires_from_now(minutes(15));
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
}
void dht_tracker::dht_status(session_status& s)
{
boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size();
s.m_dht_torrents = m_dht.data_size();
}
void dht_tracker::connection_timeout(asio::error const& e)
try
{
if (e) return;
time_duration d = m_dht.connection_timeout();
m_connection_timer.expires_from_now(d);
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::refresh_timeout(asio::error const& e)
try
{
if (e) return;
time_duration d = m_dht.refresh_timeout();
m_refresh_timer.expires_from_now(d);
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port)
{
m_socket.close();
m_socket.open(asio::ip::udp::v4());
m_socket.bind(udp::endpoint(listen_interface, listen_port));
}
void dht_tracker::tick(asio::error const& e)
try
{
if (e) return;
m_timer.expires_from_now(minutes(tick_period));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_dht.new_write_key();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
static bool first = true;
if (first)
{
boost::filesystem::create_directory("libtorrent_logs");
}
std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
m_dht.print_state(st);
// count torrents
int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
// count peers
int peers = 0;
std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
if (first)
{
first = false;
using boost::posix_time::to_simple_string;
pc << "\n\n ***** starting log at " << to_simple_string(
second_clock::universal_time()) << " *****\n\n"
<< "minute:active nodes:passive nodes"
":ping replies sent:ping queries recvd:ping"
":ping replies sent:ping queries recvd:ping"
":find_node replies bytes sent:find_node queries bytes recv"
":find_node replies bytes sent:find_node queries bytes recv"
":get_peers replies sent:get_peers queries recvd:get_peers"
":get_peers replies bytes sent:get_peers queries bytes recv"
":announce_peer replies sent:announce_peer queries recvd:announce_peer"
":announce_peer replies bytes sent:announce_peer queries bytes recv"
":error replies sent:error queries recvd:error"
":error replies bytes sent:error queries bytes recv"
":num torrents:num peers:announces per min"
":failed announces per min:total msgs per min"
":ut msgs per min:lt msgs per min:mp msgs per min"
":gr msgs per min:bytes in per sec:bytes out per sec"
":queries out bytes per sec\n\n";
}
int active;
int passive;
boost::tie(active, passive) = m_dht.size();
pc << (m_counter * tick_period)
<< "\t" << active
<< "\t" << passive;
for (int i = 0; i < 5; ++i)
pc << "\t" << (m_replies_sent[i] / float(tick_period))
<< "\t" << (m_queries_received[i] / float(tick_period))
<< "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
<< "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
pc << "\t" << torrents
<< "\t" << peers
<< "\t" << m_announces / float(tick_period)
<< "\t" << m_failed_announces / float(tick_period)
<< "\t" << (m_total_message_input / float(tick_period))
<< "\t" << (m_ut_message_input / float(tick_period))
<< "\t" << (m_lt_message_input / float(tick_period))
<< "\t" << (m_mp_message_input / float(tick_period))
<< "\t" << (m_gr_message_input / float(tick_period))
<< "\t" << (m_total_in_bytes / float(tick_period*60))
<< "\t" << (m_total_out_bytes / float(tick_period*60))
<< "\t" << (m_queries_out_bytes / float(tick_period*60))
<< std::endl;
++m_counter;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
m_total_in_bytes = 0;
m_total_out_bytes = 0;
m_queries_out_bytes = 0;
#endif
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::announce(sha1_hash const& ih, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&
, sha1_hash const&)> f)
{
m_dht.announce(ih, listen_port, f);
}
// translate bittorrent kademlia message into the generice kademlia message
// used by the library
void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred)
try
{
if (error == asio::error::operation_aborted) return;
int current_buffer = m_buffer;
m_buffer = (m_buffer + 1) & 1;
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
if (error) return;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_total_message_input;
m_total_in_bytes += bytes_transferred;
#endif
try
{
using libtorrent::entry;
using libtorrent::bdecode;
assert(bytes_transferred > 0);
entry e = bdecode(m_in_buf[current_buffer].begin()
, m_in_buf[current_buffer].end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " RECEIVED [" << m_remote_endpoint[current_buffer]
<< "]:";
#endif
libtorrent::dht::msg m;
m.message_id = 0;
m.addr = m_remote_endpoint[current_buffer];
m.transaction_id = e["t"].string();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
try
{
entry const* ver = e.find_key("v");
if (!ver) throw std::exception();
std::string const& client = ver->string();
if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
{
++m_ut_message_input;
TORRENT_LOG(dht_tracker) << " client: uTorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
{
++m_lt_message_input;
TORRENT_LOG(dht_tracker) << " client: libtorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
{
++m_mp_message_input;
TORRENT_LOG(dht_tracker) << " client: MooPolice";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
{
++m_gr_message_input;
TORRENT_LOG(dht_tracker) << " client: GetRight";
}
else
{
TORRENT_LOG(dht_tracker) << " client: generic";
}
}
catch (std::exception&)
{
TORRENT_LOG(dht_tracker) << " client: generic";
};
#endif
std::string const& msg_type = e["y"].string();
if (msg_type == "r")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: transaction: "
<< m.transaction_id;
#endif
m.reply = true;
entry const& r = e["r"];
std::string const& id = r["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
if (entry const* n = r.find_key("values"))
{
m.peers.clear();
read_endpoint_list<tcp::endpoint>(n, m.peers);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
m.nodes.clear();
if (entry const* n = r.find_key("nodes"))
{
std::string const& nodes = n->string();
std::string::const_iterator i = nodes.begin();
std::string::const_iterator end = nodes.end();
while (std::distance(i, end) >= 26)
{
node_id id;
std::copy(i, i + 20, id.begin());
i += 20;
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v4_endpoint<udp::endpoint>(i)));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
if (entry const* n = r.find_key("nodes2"))
{
entry::list_type const& contacts = n->list();
for (entry::list_type::const_iterator i = contacts.begin()
, end(contacts.end()); i != end; ++i)
{
std::string const& p = i->string();
if (p.size() < 6 + 20) continue;
std::string::const_iterator in = p.begin();
node_id id;
std::copy(in, in + 20, id.begin());
in += 20;
if (p.size() == 6 + 20)
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v4_endpoint<udp::endpoint>(in)));
else if (p.size() == 18 + 20)
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v6_endpoint<udp::endpoint>(in)));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size();
#endif
}
entry const* token = r.find_key("token");
if (token) m.write_token = *token;
}
else if (msg_type == "q")
{
m.reply = false;
entry const& a = e["a"];
std::string const& id = a["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
std::string request_kind(e["q"].string());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: " << request_kind;
#endif
if (request_kind == "ping")
{
m.message_id = libtorrent::dht::messages::ping;
}
else if (request_kind == "find_node")
{
std::string const& target = a["target"].string();
if (target.size() != 20) throw std::runtime_error("invalid size of target id");
std::copy(target.begin(), target.end(), m.info_hash.begin());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
m.message_id = libtorrent::dht::messages::find_node;
}
else if (request_kind == "get_peers")
{
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.message_id = libtorrent::dht::messages::get_peers;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
}
else if (request_kind == "announce_peer")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_announces;
#endif
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20)
throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.port = a["port"].integer();
m.write_token = a["token"];
m.message_id = libtorrent::dht::messages::announce_peer;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
TORRENT_LOG(dht_tracker) << " port: " << m.port;
if (!m_dht.verify_token(m))
++m_failed_announces;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
<< request_kind;
#endif
throw std::runtime_error("unsupported request: " + request_kind);
}
}
else if (msg_type == "e")
{
entry::list_type const& list = e["e"].list();
m.message_id = messages::error;
m.error_msg = list.back().string();
m.error_code = list.front().integer();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
<< msg_type;
#endif
throw std::runtime_error("unsupported message type: " + msg_type);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
if (!m.reply)
{
++m_queries_received[m.message_id];
m_queries_bytes_received[m.message_id] += int(bytes_transferred);
}
TORRENT_LOG(dht_tracker) << e;
#endif
m_dht.incoming(m);
}
catch (std::exception& e)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
<< e.what();
#endif
}
}
catch (std::exception& e)
{
assert(false);
};
entry dht_tracker::state() const
{
entry ret(entry::dictionary_t);
{
entry nodes(entry::list_t);
for (node_impl::iterator i(m_dht.begin())
, end(m_dht.end()); i != end; ++i)
{
std::string node;
std::back_insert_iterator<std::string> out(node);
write_endpoint(i->addr, out);
nodes.list().push_back(entry(node));
}
bucket_t cache;
m_dht.replacement_cache(cache);
for (bucket_t::iterator i(cache.begin())
, end(cache.end()); i != end; ++i)
{
std::string node;
std::back_insert_iterator<std::string> out(node);
write_endpoint(i->addr, out);
nodes.list().push_back(entry(node));
}
if (!nodes.list().empty())
ret["nodes"] = nodes;
}
ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
return ret;
}
void dht_tracker::add_node(udp::endpoint node)
{
m_dht.add_node(node);
}
void dht_tracker::add_node(std::pair<std::string, int> const& node)
{
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup
, this, _1, _2));
}
void dht_tracker::on_name_lookup(asio::error const& e
, udp::resolver::iterator host) try
{
if (e || host == udp::resolver::iterator()) return;
add_node(host->endpoint());
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
{
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup
, this, _1, _2));
}
void dht_tracker::on_router_name_lookup(asio::error const& e
, udp::resolver::iterator host) try
{
if (e || host == udp::resolver::iterator()) return;
m_dht.add_router_node(host->endpoint());
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::on_bootstrap()
{}
void dht_tracker::send_packet(msg const& m)
{
using libtorrent::bencode;
using libtorrent::entry;
entry e(entry::dictionary_t);
e["t"] = m.transaction_id;
std::string version_str("LT ");
std::string::iterator i = version_str.begin() + 2;
detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i);
detail::write_uint8(LIBTORRENT_VERSION_MINOR, i);
e["v"] = version_str;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " SENDING [" << m.addr << "]:";
TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id;
// e.print(std::cerr);
#endif
if (m.message_id == messages::error)
{
assert(m.reply);
e["y"] = "e";
entry error_list(entry::list_t);
error_list.list().push_back(entry(m.error_code));
error_list.list().push_back(entry(m.error_msg));
e["e"] = error_list;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else if (m.reply)
{
e["y"] = "r";
e["r"] = entry(entry::dictionary_t);
entry& r = e["r"];
r["id"] = std::string(m.id.begin(), m.id.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: "
<< messages::ids[m.message_id];
#endif
if (m.write_token.type() != entry::undefined_t)
r["token"] = m.write_token;
switch (m.message_id)
{
case messages::ping:
break;
case messages::find_node:
{
bool ipv6_nodes = false;
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
if (!i->addr.address().is_v4())
{
ipv6_nodes = true;
continue;
}
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
if (ipv6_nodes)
{
r["nodes2"] = entry(entry::list_t);
entry& p = r["nodes2"];
std::string endpoint;
endpoint.resize(6);
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
std::string::iterator out = endpoint.begin();
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
p.list().push_back(entry(endpoint));
}
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
break;
}
case messages::get_peers:
{
if (m.peers.empty())
{
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
if (!i->addr.address().is_v4()) continue;
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
else
{
r["values"] = entry(entry::list_t);
entry& p = r["values"];
std::string endpoint;
endpoint.resize(6);
for (msg::peers_t::const_iterator i = m.peers.begin()
, end(m.peers.end()); i != end; ++i)
{
std::string::iterator out = endpoint.begin();
write_endpoint(*i, out);
p.list().push_back(entry(endpoint));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
break;
}
case messages::announce_peer:
break;
break;
}
}
else
{
e["y"] = "q";
e["a"] = entry(entry::dictionary_t);
entry& a = e["a"];
a["id"] = std::string(m.id.begin(), m.id.end());
if (m.write_token.type() != entry::undefined_t)
a["token"] = m.write_token;
assert(m.message_id <= messages::error);
e["q"] = messages::ids[m.message_id];
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: "
<< messages::ids[m.message_id];
#endif
switch (m.message_id)
{
case messages::find_node:
{
a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::get_peers:
{
a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::announce_peer:
a["port"] = m_settings.service_port;
a["info_hash"] = boost::lexical_cast<std::string>(m.info_hash);
a["token"] = m.write_token;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " port: "
<< m_settings.service_port
<< " info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
default: break;
}
}
m_send_buf.clear();
bencode(std::back_inserter(m_send_buf), e);
m_socket.send_to(asio::buffer(&m_send_buf[0]
, (int)m_send_buf.size()), m.addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
m_total_out_bytes += m_send_buf.size();
if (m.reply)
{
++m_replies_sent[m.message_id];
m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
}
else
{
m_queries_out_bytes += m_send_buf.size();
}
TORRENT_LOG(dht_tracker) << e;
#endif
if (!m.piggy_backed_ping) return;
msg pm;
pm.reply = false;
pm.piggy_backed_ping = false;
pm.message_id = messages::ping;
pm.transaction_id = m.ping_transaction_id;
pm.id = m.id;
pm.addr = m.addr;
send_packet(pm);
}
}}

156
cpp/kademlia/find_data.cpp Normal file
View File

@@ -0,0 +1,156 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/io.hpp>
namespace libtorrent { namespace dht
{
typedef boost::shared_ptr<observer> observer_ptr;
class find_data_observer : public observer
{
public:
find_data_observer(
boost::intrusive_ptr<find_data> const& algorithm
, node_id self
, node_id target)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& m)
{
m.reply = false;
m.message_id = messages::get_peers;
m.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<find_data> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void find_data_observer::reply(msg const& m)
{
if (!m.peers.empty())
{
m_algorithm->got_data(&m);
}
else
{
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void find_data_observer::timeout()
{
m_algorithm->failed(m_self);
}
find_data::find_data(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
, m_done(false)
{
boost::intrusive_ptr<find_data> self(this);
add_requests();
}
void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
{
if (m_done)
{
m_invoke_count = -1;
return;
}
observer_ptr p(new find_data_observer(this, id, m_target));
m_rpc.invoke(messages::get_peers, addr, p);
}
void find_data::got_data(msg const* m)
{
m_done = true;
m_done_callback(m);
}
void find_data::done()
{
if (m_invoke_count != 0) return;
if (!m_done) m_done_callback(0);
}
void find_data::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
std::cerr << "find_data::initiate, key: " << target << "\n";
new find_data(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

539
cpp/kademlia/node.cpp Normal file
View File

@@ -0,0 +1,539 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <utility>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/function.hpp>
#include <boost/iterator_adaptors.hpp>
#include "libtorrent/io.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/random_sample.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/rpc_manager.hpp"
#include "libtorrent/kademlia/packet_iterator.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/refresh.hpp"
#include "libtorrent/kademlia/closest_nodes.hpp"
#include "libtorrent/kademlia/find_data.hpp"
using boost::bind;
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
namespace libtorrent { namespace dht
{
#ifdef _MSC_VER
namespace
{
char rand() { return (char)std::rand(); }
}
#endif
typedef boost::shared_ptr<observer> observer_ptr;
// TODO: configurable?
enum { announce_interval = 30 };
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(node)
#endif
node_id generate_id()
{
char random[20];
std::srand(std::time(0));
#ifdef _MSC_VER
std::generate(random, random + 20, &rand);
#else
std::generate(random, random + 20, &std::rand);
#endif
hasher h;
h.update(random, 20);
return h.final();
}
// remove peers that have timed out
void purge_peers(std::set<peer_entry>& peers)
{
for (std::set<peer_entry>::iterator i = peers.begin()
, end(peers.end()); i != end;)
{
// the peer has timed out
if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time())
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "peer timed out at: " << i->addr.address();
#endif
peers.erase(i++);
}
else
++i;
}
}
void nop() {}
node_impl::node_impl(boost::function<void(msg const&)> const& f
, dht_settings const& settings, boost::optional<node_id> node_id)
: m_settings(settings)
, m_id(node_id ? *node_id : generate_id())
, m_table(m_id, 8, settings)
, m_rpc(bind(&node_impl::incoming_request, this, _1)
, m_id, m_table, f)
, m_last_tracker_tick(boost::posix_time::second_clock::universal_time())
{
m_secret[0] = std::rand();
m_secret[1] = std::rand();
}
bool node_impl::verify_token(msg const& m)
{
if (m.write_token.type() != entry::string_t)
return false;
std::string const& token = m.write_token.string();
if (token.length() != 4) return false;
hasher h1;
std::string address = m.addr.address().to_string();
h1.update(&address[0], address.length());
h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
h1.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash h = h1.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
hasher h2;
h2.update(&address[0], address.length());
h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
h = h2.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
return false;
}
entry node_impl::generate_token(msg const& m)
{
std::string token;
token.resize(4);
hasher h;
std::string address = m.addr.address().to_string();
h.update(&address[0], address.length());
h.update((char*)&m_secret[0], sizeof(m_secret[0]));
h.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash hash = h.final();
std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]);
return entry(token);
}
void node_impl::refresh(node_id const& id
, boost::function0<void> f)
{
// use the 'bucket size' closest nodes
// to start the refresh with
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(id, start, false);
refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::bootstrap(std::vector<udp::endpoint> const& nodes
, boost::function0<void> f)
{
std::vector<node_entry> start;
start.reserve(nodes.size());
std::copy(nodes.begin(), nodes.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::refresh()
{
std::vector<node_entry> start;
start.reserve(m_table.size().get<0>());
std::copy(m_table.begin(), m_table.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
}
int node_impl::bucket_size(int bucket)
{
return m_table.bucket_size(bucket);
}
void node_impl::new_write_key()
{
m_secret[1] = m_secret[0];
m_secret[0] = std::rand();
}
void node_impl::refresh_bucket(int bucket)
{
assert(bucket >= 0 && bucket < 160);
// generate a random node_id within the given bucket
node_id target = generate_id();
int num_bits = 160 - bucket;
node_id mask(0);
for (int i = 0; i < num_bits; ++i)
{
int byte = i / 8;
mask[byte] |= 0x80 >> (i % 8);
}
node_id root = m_id;
root &= mask;
target &= ~mask;
target |= root;
// make sure this is in another subtree than m_id
// clear the (num_bits - 1) bit and then set it to the
// inverse of m_id's corresponding bit.
target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8));
target[(num_bits - 1) / 8] |=
(~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8));
assert(distance_exp(m_id, target) == bucket);
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(target, start, false, m_table.bucket_size());
refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
m_table.touch_bucket(bucket);
}
void node_impl::incoming(msg const& m)
{
if (m_rpc.incoming(m))
{
refresh();
}
}
namespace
{
class announce_observer : public observer
{
public:
announce_observer(sha1_hash const& info_hash, int listen_port
, entry const& write_token)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_token(write_token)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
m.write_token = m_token;
}
void timeout() {}
void reply(msg const&) {}
private:
sha1_hash m_info_hash;
int m_listen_port;
entry m_token;
};
class get_peers_observer : public observer
{
public:
get_peers_observer(sha1_hash const& info_hash, int listen_port
, rpc_manager& rpc
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_rpc(rpc)
, m_fun(f)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
}
void timeout() {}
void reply(msg const& r)
{
m_rpc.invoke(messages::announce_peer, r.addr
, boost::shared_ptr<observer>(
new announce_observer(m_info_hash, m_listen_port, r.write_token)));
m_fun(r.peers, m_info_hash);
}
private:
sha1_hash m_info_hash;
int m_listen_port;
rpc_manager& m_rpc;
boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
};
void announce_fun(std::vector<node_entry> const& v, rpc_manager& rpc
, int listen_port, sha1_hash const& ih
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
bool nodes = false;
// only store on the first k nodes
for (std::vector<node_entry>::const_iterator i = v.begin()
, end(v.end()); i != end; ++i)
{
rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr<observer>(
new get_peers_observer(ih, listen_port, rpc, f)));
nodes = true;
}
}
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void node_impl::add_router_node(udp::endpoint router)
{
m_table.add_router_node(router);
}
void node_impl::add_node(udp::endpoint node)
{
// ping the node, and if we get a reply, it
// will be added to the routing table
observer_ptr p(new dummy_observer());
m_rpc.invoke(messages::ping, node, p);
}
void node_impl::announce(sha1_hash const& info_hash, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
// search for nodes with ids close to id, and then invoke the
// get_peers and then announce_peer rpc on them.
closest_nodes::initiate(info_hash, m_settings.search_branching
, m_table.bucket_size(), m_table, m_rpc
, boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port
, info_hash, f));
}
time_duration node_impl::refresh_timeout()
{
int refresh = -1;
ptime now = second_clock::universal_time();
ptime next = now + minutes(15);
for (int i = 0; i < 160; ++i)
{
ptime r = m_table.next_refresh(i);
if (r <= now)
{
if (refresh == -1) refresh = i;
}
else if (r < next)
{
next = r;
}
}
if (refresh != -1)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "refreshing bucket: " << refresh;
#endif
refresh_bucket(refresh);
}
if (next < now + seconds(5)) return seconds(5);
return next - now;
}
time_duration node_impl::connection_timeout()
{
time_duration d = m_rpc.tick();
ptime now(second_clock::universal_time());
if (now - m_last_tracker_tick < minutes(10)) return d;
m_last_tracker_tick = now;
// look through all peers and see if any have timed out
for (data_iterator i = begin_data(), end(end_data()); i != end;)
{
torrent_entry& t = i->second;
node_id const& key = i->first;
++i;
purge_peers(t.peers);
// if there are no more peers, remove the entry altogether
if (t.peers.empty())
{
table_t::iterator i = m_map.find(key);
if (i != m_map.end()) m_map.erase(i);
}
}
return d;
}
void node_impl::on_announce(msg const& m, msg& reply)
{
if (!verify_token(m))
{
reply.message_id = messages::error;
reply.error_code = 203;
reply.error_msg = "Incorrect write token in announce_peer message";
return;
}
// the token was correct. That means this
// node is not spoofing its address. So, let
// the table get a chance to add it.
m_table.node_seen(m.id, m.addr);
torrent_entry& v = m_map[m.info_hash];
peer_entry e;
e.addr = tcp::endpoint(m.addr.address(), m.addr.port());
e.added = second_clock::universal_time();
std::set<peer_entry>::iterator i = v.peers.find(e);
if (i != v.peers.end()) v.peers.erase(i++);
v.peers.insert(i, e);
}
namespace
{
tcp::endpoint get_endpoint(peer_entry const& p)
{
return p.addr;
}
}
bool node_impl::on_find(msg const& m, std::vector<tcp::endpoint>& peers) const
{
table_t::const_iterator i = m_map.find(m.info_hash);
if (i == m_map.end()) return false;
torrent_entry const& v = i->second;
int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
peers.clear();
peers.reserve(num);
random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint)
, boost::make_transform_iterator(v.peers.end(), &get_endpoint)
, std::back_inserter(peers), num);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<tcp::endpoint>::iterator i = peers.begin()
, end(peers.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << *i;
}
#endif
return true;
}
void node_impl::incoming_request(msg const& m)
{
msg reply;
switch (m.message_id)
{
case messages::ping:
break;
case messages::get_peers:
{
reply.info_hash = m.info_hash;
reply.write_token = generate_token(m);
if (!on_find(m, reply.peers))
{
// we don't have any peers for this info_hash,
// return nodes instead
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
}
break;
case messages::find_node:
{
reply.info_hash = m.info_hash;
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
break;
case messages::announce_peer:
{
on_announce(m, reply);
}
break;
};
if (m_table.need_node(m.id))
m_rpc.reply_with_ping(reply, m);
else
m_rpc.reply(reply, m);
}
} } // namespace libtorrent::dht

97
cpp/kademlia/node_id.cpp Normal file
View File

@@ -0,0 +1,97 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <iomanip>
#include <cassert>
#include <boost/bind.hpp>
#include "libtorrent/kademlia/node_id.hpp"
using boost::bind;
namespace libtorrent { namespace dht
{
// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
node_id ret;
node_id::iterator k = ret.begin();
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, ++k)
{
*k = *i ^ *j;
}
return ret;
}
// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
{
boost::uint8_t lhs = (*i ^ *k);
boost::uint8_t rhs = (*j ^ *k);
if (lhs < rhs) return true;
if (lhs > rhs) return false;
}
return false;
}
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
int byte = node_id::size - 1;
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, --byte)
{
assert(byte >= 0);
boost::uint8_t t = *i ^ *j;
if (t == 0) continue;
// we have found the first non-zero byte
// return the bit-number of the first bit
// that differs
int bit = byte * 8;
for (int b = 7; b > 0; --b)
if (t >= (1 << b)) return bit + b;
return bit;
}
return 0;
}
} } // namespace libtorrent::dht

190
cpp/kademlia/refresh.cpp Normal file
View File

@@ -0,0 +1,190 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/refresh.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/io.hpp>
#include <boost/bind.hpp>
using boost::bind;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(refresh)
#endif
typedef boost::shared_ptr<observer> observer_ptr;
class refresh_observer : public observer
{
public:
refresh_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
, node_id target
)
: m_target(target)
, m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& m)
{
m.info_hash = m_target;
}
void timeout();
void reply(msg const& m);
private:
node_id const m_target;
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void refresh_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void refresh_observer::timeout()
{
m_algorithm->failed(m_self);
}
class ping_observer : public observer
{
public:
ping_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
)
: m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& p) {}
void timeout();
void reply(msg const& m);
private:
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void ping_observer::reply(msg const& m)
{
m_algorithm->ping_reply(m_self);
}
void ping_observer::timeout()
{
m_algorithm->ping_timeout(m_self);
}
void refresh::invoke(node_id const& nid, udp::endpoint addr)
{
observer_ptr p(new refresh_observer(
this
, nid
, m_target
));
m_rpc.invoke(messages::find_node, addr, p);
}
void refresh::done()
{
m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ?
m_results.begin() + m_max_results : m_results.end();
invoke_pings_or_finish();
}
void refresh::ping_reply(node_id nid)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::ping_timeout(node_id nid)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::invoke_pings_or_finish()
{
while (m_active_pings < m_max_active_pings)
{
if (m_leftover_nodes_iterator == m_results.end()) break;
result const& node = *m_leftover_nodes_iterator;
// Skip initial nodes
if (node.flags & result::initial)
{
++m_leftover_nodes_iterator;
continue;
}
observer_ptr p(new ping_observer(this, node.id));
m_rpc.invoke(messages::ping, node.addr, p);
++m_active_pings;
++m_leftover_nodes_iterator;
}
if (m_active_pings == 0)
{
m_done_callback();
}
}
} } // namespace libtorrent::dht

View File

@@ -0,0 +1,434 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <deque>
#include <algorithm>
#include <functional>
#include <numeric>
#include <boost/cstdint.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/session_settings.hpp"
using boost::bind;
using boost::uint8_t;
using boost::posix_time::second_clock;
using boost::posix_time::minutes;
using boost::posix_time::seconds;
using boost::posix_time::hours;
namespace pt = boost::posix_time;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef asio::ip::address_v4 address;
routing_table::routing_table(node_id const& id, int bucket_size
, dht_settings const& settings)
: m_bucket_size(bucket_size)
, m_settings(settings)
, m_id(id)
, m_lowest_active_bucket(160)
{
// distribute the refresh times for the buckets in an
// attempt do even out the network load
for (int i = 0; i < 160; ++i)
m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5);
}
boost::tuple<int, int> routing_table::size() const
{
int nodes = 0;
int replacements = 0;
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
nodes += i->first.size();
replacements += i->second.size();
}
return boost::make_tuple(nodes, replacements);
}
void routing_table::print_state(std::ostream& os) const
{
os << "kademlia routing table state\n"
<< "bucket_size: " << m_bucket_size << "\n"
<< "node_id: " << m_id << "\n\n";
os << "number of nodes per bucket:\n"
"live\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->first.size()) > (7 - k) ? "|" : " ");
}
os << "\n";
}
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << "+";
}
os << "\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->second.size()) > k ? "|" : " ");
}
os << "\n";
}
os << "cached\n-----------\n";
os << "nodes:\n";
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
int bucket_index = int(i - m_buckets.begin());
os << "bucket " << bucket_index << " "
<< to_simple_string(m_bucket_activity[bucket_index])
<< " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
<< "\n";
for (bucket_t::const_iterator j = i->first.begin()
, end(i->first.end()); j != end; ++j)
{
os << "ip: " << j->addr << " fails: " << j->fail_count
<< " id: " << j->id << "\n";
}
}
}
void routing_table::touch_bucket(int bucket)
{
m_bucket_activity[bucket] = second_clock::universal_time();
}
boost::posix_time::ptime routing_table::next_refresh(int bucket)
{
assert(bucket < 160);
assert(bucket >= 0);
// lower than or equal to since a refresh of bucket 0 will
// effectively refresh the lowest active bucket as well
if (bucket <= m_lowest_active_bucket && bucket > 0)
return second_clock::universal_time() + minutes(15);
return m_bucket_activity[bucket] + minutes(15);
}
void routing_table::replacement_cache(bucket_t& nodes) const
{
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
std::copy(i->second.begin(), i->second.end()
, std::back_inserter(nodes));
}
}
bool routing_table::need_node(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
// if the replacement cache is full, we don't
// need another node. The table is fine the
// way it is.
if ((int)rb.size() >= m_bucket_size) return false;
// if the node already exists, we don't need it
if (std::find_if(b.begin(), b.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != b.end()) return false;
if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != rb.end()) return false;
return true;
}
void routing_table::node_failed(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
if (i == b.end()) return;
// if messages to ourself fails, ignore it
if (bucket_index == 0) return;
if (rb.empty())
{
++i->fail_count;
if (i->fail_count >= m_settings.max_fail_count)
{
b.erase(i);
assert(m_lowest_active_bucket <= bucket_index);
while (m_buckets[m_lowest_active_bucket].first.empty()
&& m_lowest_active_bucket < 160)
{
++m_lowest_active_bucket;
}
}
return;
}
b.erase(i);
b.push_back(rb.back());
rb.erase(rb.end() - 1);
}
void routing_table::add_router_node(udp::endpoint router)
{
m_router_nodes.insert(router);
}
// this function is called every time the node sees
// a sign of a node being alive. This node will either
// be inserted in the k-buckets or be moved to the top
// of its bucket.
// the return value indicates if the table needs a refresh.
// if true, the node should refresh the table (i.e. do a find_node
// on its own id)
bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
{
if (m_router_nodes.find(addr) != m_router_nodes.end()) return false;
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
bool ret = need_bootstrap();
m_bucket_activity[bucket_index] = second_clock::universal_time();
if (i != b.end())
{
// TODO: what do we do if we see a node with
// the same id as a node at a different address?
// assert(i->addr == addr);
// we already have the node in our bucket
// just move it to the back since it was
// the last node we had any contact with
// in this bucket
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
return ret;
}
// if the node was not present in our list
// we will only insert it if there is room
// for it, or if some of our nodes have gone
// offline
if ((int)b.size() < m_bucket_size)
{
b.push_back(node_entry(id, addr));
// if bucket index is 0, the node is ourselves
// don't updated m_lowest_active_bucket
if (bucket_index < m_lowest_active_bucket
&& bucket_index > 0)
m_lowest_active_bucket = bucket_index;
// TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
return ret;
}
// if there is no room, we look for nodes marked as stale
// in the k-bucket. If we find one, we can replace it.
// A node is considered stale if it has failed at least one
// time. Here we choose the node that has failed most times.
// If we don't find one, place this node in the replacement-
// cache and replace any nodes that will fail in the future
// with nodes from that cache.
i = std::max_element(b.begin(), b.end()
, bind(std::less<int>()
, bind(&node_entry::fail_count, _1)
, bind(&node_entry::fail_count, _2)));
if (i != b.end() && i->fail_count > 0)
{
// i points to a node that has been marked
// as stale. Replace it with this new one
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
return ret;
}
// if we don't have any identified stale nodes in
// the bucket, and the bucket is full, we have to
// cache this node and wait until some node fails
// and then replace it.
bucket_t& rb = m_buckets[bucket_index].second;
i = std::find_if(rb.begin(), rb.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
// if the node is already in the replacement bucket
// just return.
if (i != rb.end()) return ret;
if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
rb.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
return ret;
}
bool routing_table::need_bootstrap() const
{
for (const_iterator i = begin(); i != end(); ++i)
{
if (i->fail_count == 0) return false;
}
return true;
}
// fills the vector with the k nodes from our buckets that
// are nearest to the given id.
void routing_table::find_node(node_id const& target
, std::vector<node_entry>& l, bool include_self, int count)
{
l.clear();
if (count == 0) count = m_bucket_size;
l.reserve(count);
int bucket_index = distance_exp(m_id, target);
bucket_t& b = m_buckets[bucket_index].first;
// copy all nodes that hasn't failed into the target
// vector.
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
assert((int)l.size() <= count);
if ((int)l.size() == count)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
// if we didn't have enough nodes in that bucket
// we have to reply with nodes from buckets closer
// to us. i.e. all the buckets in the range
// [0, bucket_index) if we are to include ourself
// or [1, bucket_index) if not.
bucket_t tmpb;
for (int i = include_self?0:1; i < count; ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
, bind(&node_entry::fail_count, _1));
}
std::random_shuffle(tmpb.begin(), tmpb.end());
size_t to_copy = (std::min)(m_bucket_size - l.size()
, tmpb.size());
std::copy(tmpb.begin(), tmpb.begin() + to_copy
, std::back_inserter(l));
assert((int)l.size() <= m_bucket_size);
// return if we have enough nodes or if the bucket index
// is the biggest index available (there are no more buckets)
// to look in.
if ((int)l.size() == count
|| bucket_index == (int)m_buckets.size() - 1)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
if ((int)l.size() >= count)
{
l.erase(l.begin() + count, l.end());
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
}
assert((int)l.size() == count
|| std::distance(l.begin(), l.end()) < m_bucket_size);
assert((int)l.size() <= count);
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
}
routing_table::iterator routing_table::begin() const
{
return iterator(m_buckets.begin(), m_buckets.end());
}
routing_table::iterator routing_table::end() const
{
return iterator(m_buckets.end(), m_buckets.end());
}
} } // namespace libtorrent::dht

View File

@@ -0,0 +1,356 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/bind.hpp>
#include <libtorrent/io.hpp>
#include <libtorrent/invariant_check.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/hasher.hpp>
#include <fstream>
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::milliseconds;
using boost::shared_ptr;
using boost::bind;
namespace libtorrent { namespace dht
{
namespace io = libtorrent::detail;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(rpc)
#endif
node_id generate_id();
rpc_manager::rpc_manager(fun const& f, node_id const& our_id
, routing_table& table, send_fun const& sf)
: m_next_transaction_id(rand() % max_transactions)
, m_oldest_transaction_id(m_next_transaction_id)
, m_incoming(f)
, m_send(sf)
, m_our_id(our_id)
, m_table(table)
, m_timer(boost::posix_time::microsec_clock::universal_time())
, m_random_number(generate_id())
{
std::srand(time(0));
}
rpc_manager::~rpc_manager()
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Destructing";
#endif
}
#ifndef NDEBUG
void rpc_manager::check_invariant() const
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
assert(m_next_transaction_id >= 0);
assert(m_next_transaction_id < max_transactions);
assert(!m_transactions[m_next_transaction_id]);
for (int i = (m_next_transaction_id + 1) % max_transactions;
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
}
#endif
bool rpc_manager::incoming(msg const& m)
{
INVARIANT_CHECK;
if (m.reply)
{
// if we don't have the transaction id in our
// request list, ignore the packet
if (m.transaction_id.size() != 2)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
<< m.transaction_id.size() << " from " << m.addr;
#endif
return false;
}
std::string::const_iterator i = m.transaction_id.begin();
int tid = io::read_uint16(i);
if (tid >= (int)m_transactions.size()
|| tid < 0)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
boost::shared_ptr<observer> o = m_transactions[tid];
if (!o)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
if (m.addr != o->target_addr)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
- o->sent).total_milliseconds() << std::endl;
#endif
o->reply(m);
m_transactions[tid].reset();
if (m.piggy_backed_ping)
{
// there is a ping request piggy
// backed in this reply
msg ph;
ph.message_id = messages::ping;
ph.transaction_id = m.ping_transaction_id;
ph.id = m_our_id;
ph.addr = m.addr;
msg empty;
reply(empty, ph);
}
return m_table.node_seen(m.id, m.addr);
}
else
{
// this is an incoming request
m_incoming(m);
}
return false;
}
time_duration rpc_manager::tick()
{
INVARIANT_CHECK;
using boost::posix_time::microsec_clock;
const int timeout_ms = 20 * 1000;
// look for observers that has timed out
if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);
for (;m_next_transaction_id != m_oldest_transaction_id;
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
if (!o) continue;
time_duration diff = o->sent + milliseconds(timeout_ms)
- microsec_clock::universal_time();
if (diff > seconds(0))
{
if (diff < seconds(1)) return seconds(1);
return diff;
}
m_transactions[m_oldest_transaction_id].reset();
o->timeout();
}
return milliseconds(timeout_ms);
}
unsigned int rpc_manager::new_transaction_id()
{
INVARIANT_CHECK;
unsigned int tid = m_next_transaction_id;
m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
// boost::shared_ptr<observer> o = m_transactions[m_next_transaction_id];
if (m_transactions[m_next_transaction_id])
{
m_transactions[m_next_transaction_id].reset();
assert(m_oldest_transaction_id == m_next_transaction_id);
}
if (m_oldest_transaction_id == m_next_transaction_id)
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
" messages! limit: " << (int)max_transactions;
#endif
update_oldest_transaction_id();
}
#ifndef NDEBUG
assert(!m_transactions[m_next_transaction_id]);
for (int i = (m_next_transaction_id + 1) % max_transactions;
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
#endif
// hopefully this wouldn't happen, but unfortunately, the
// traversal algorithm will simply fail in case its connections
// are overwritten. If timeout() is called, it will likely spawn
// another connection, which in turn will close the next one
// and so on.
// if (o) o->timeout();
return tid;
}
void rpc_manager::update_oldest_transaction_id()
{
INVARIANT_CHECK;
assert(m_oldest_transaction_id != m_next_transaction_id);
while (!m_transactions[m_oldest_transaction_id])
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1)
% max_transactions;
if (m_oldest_transaction_id == m_next_transaction_id)
break;
}
}
void rpc_manager::invoke(int message_id, udp::endpoint target_addr
, shared_ptr<observer> o)
{
INVARIANT_CHECK;
msg m;
m.message_id = message_id;
m.reply = false;
m.id = m_our_id;
m.addr = target_addr;
int tid = new_transaction_id();
m.transaction_id.clear();
std::back_insert_iterator<std::string> out(m.transaction_id);
io::write_uint16(tid, out);
o->send(m);
m_transactions[tid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = target_addr;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
<< " -> " << target_addr;
#endif
m_send(m);
}
void rpc_manager::reply(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = false;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
m_send(m);
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = true;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
int ptid = new_transaction_id();
m.ping_transaction_id.clear();
std::back_insert_iterator<std::string> out(m.ping_transaction_id);
io::write_uint16(ptid, out);
boost::shared_ptr<observer> o(new dummy_observer);
m_transactions[ptid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = m.addr;
m_send(m);
}
} } // namespace libtorrent::dht

View File

@@ -0,0 +1,162 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <boost/bind.hpp>
using boost::bind;
using asio::ip::udp;
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(traversal)
#endif
void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
{
if (m_failed.find(addr) != m_failed.end()) return;
result const entry(id, addr, flags);
std::vector<result>::iterator i = std::lower_bound(
m_results.begin()
, m_results.end()
, entry
, bind(
compare_ref
, bind(&result::id, _1)
, bind(&result::id, _2)
, m_target
)
);
if (i == m_results.end() || i->id != id)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "adding result: " << id << " " << addr;
#endif
m_results.insert(i, entry);
}
}
void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
{
add_entry(id, addr, 0);
}
void traversal_algorithm::finished(node_id const& id)
{
--m_invoke_count;
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::failed(node_id const& id)
{
m_invoke_count--;
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, m_results.end()
, bind(
std::equal_to<node_id>()
, bind(&result::id, _1)
, id
)
);
assert(i != m_results.end());
assert(i->flags & result::queried);
m_failed.insert(i->addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr;
#endif
m_results.erase(i);
m_table.node_failed(id);
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr)
{
invoke(id, addr);
++m_invoke_count;
}
namespace
{
bool bitwise_nand(unsigned char lhs, unsigned char rhs)
{
return (lhs & rhs) == 0;
}
}
void traversal_algorithm::add_requests()
{
while (m_invoke_count < m_branch_factor)
{
// Find the first node that hasn't already been queried.
// TODO: Better heuristic
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, last_iterator()
, bind(
&bitwise_nand
, bind(&result::flags, _1)
, (unsigned char)result::queried
)
);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i);
#endif
if (i == last_iterator()) break;
add_request(i->id, i->addr);
i->flags |= result::queried;
}
}
std::vector<traversal_algorithm::result>::iterator traversal_algorithm::last_iterator()
{
return (int)m_results.size() >= m_max_results ?
m_results.begin() + m_max_results
: m_results.end();
}
} } // namespace libtorrent::dht

2050
cpp/peer_connection.cpp Executable file

File diff suppressed because it is too large Load Diff

1198
cpp/piece_picker.cpp Executable file

File diff suppressed because it is too large Load Diff

1419
cpp/policy.cpp Executable file

File diff suppressed because it is too large Load Diff

287
cpp/session.cpp Executable file
View File

@@ -0,0 +1,287 @@
/*
Copyright (c) 2006, Arvid Norberg, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/limits.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/fingerprint.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/file.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
using namespace boost::posix_time;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::bind;
using boost::mutex;
using libtorrent::aux::session_impl;
namespace libtorrent
{
session::session(
fingerprint const& id
, std::pair<int, int> listen_port_range
, char const* listen_interface)
: m_impl(new session_impl(listen_port_range, id, listen_interface))
{
// turn off the filename checking in boost.filesystem
using namespace boost::filesystem;
if (path::default_name_check_writable())
path::default_name_check(no_check);
assert(listen_port_range.first > 0);
assert(listen_port_range.first < listen_port_range.second);
#ifndef NDEBUG
// this test was added after it came to my attention
// that devstudios managed c++ failed to generate
// correct code for boost.function
boost::function0<void> test = boost::ref(*m_impl);
assert(!test.empty());
#endif
}
session::session(fingerprint const& id)
: m_impl(new session_impl(std::make_pair(0, 0), id))
{
#ifndef NDEBUG
boost::function0<void> test = boost::ref(*m_impl);
assert(!test.empty());
#endif
}
session::~session()
{
assert(m_impl);
// if there is at least one destruction-proxy
// abort the session and let the destructor
// of the proxy to syncronize
if (!m_impl.unique())
m_impl->abort();
}
void session::disable_extensions()
{
m_impl->disable_extensions();
}
void session::set_ip_filter(ip_filter const& f)
{
m_impl->set_ip_filter(f);
}
void session::set_peer_id(peer_id const& id)
{
m_impl->set_peer_id(id);
}
void session::set_key(int key)
{
m_impl->set_key(key);
}
void session::enable_extension(extension_index i)
{
m_impl->enable_extension(i);
}
std::vector<torrent_handle> session::get_torrents() const
{
return m_impl->get_torrents();
}
// if the torrent already exists, this will throw duplicate_torrent
torrent_handle session::add_torrent(
torrent_info const& ti
, boost::filesystem::path const& save_path
, entry const& resume_data
, bool compact_mode
, int block_size)
{
return m_impl->add_torrent(ti, save_path, resume_data
, compact_mode, block_size);
}
torrent_handle session::add_torrent(
char const* tracker_url
, sha1_hash const& info_hash
, boost::filesystem::path const& save_path
, entry const& e
, bool compact_mode
, int block_size)
{
return m_impl->add_torrent(tracker_url, info_hash, save_path, e
, compact_mode, block_size);
}
void session::remove_torrent(const torrent_handle& h)
{
m_impl->remove_torrent(h);
}
bool session::listen_on(
std::pair<int, int> const& port_range
, const char* net_interface)
{
return m_impl->listen_on(port_range, net_interface);
}
unsigned short session::listen_port() const
{
return m_impl->listen_port();
}
session_status session::status() const
{
return m_impl->status();
}
#ifndef TORRENT_DISABLE_DHT
void session::start_dht(entry const& startup_state)
{
m_impl->start_dht(startup_state);
}
void session::stop_dht()
{
m_impl->stop_dht();
}
void session::set_dht_settings(dht_settings const& settings)
{
m_impl->set_dht_settings(settings);
}
entry session::dht_state() const
{
return m_impl->dht_state();
}
void session::add_dht_node(std::pair<std::string, int> const& node)
{
m_impl->add_dht_node(node);
}
void session::add_dht_router(std::pair<std::string, int> const& node)
{
m_impl->add_dht_router(node);
}
#endif
bool session::is_listening() const
{
return m_impl->is_listening();
}
void session::set_settings(session_settings const& s)
{
m_impl->set_settings(s);
}
session_settings const& session::settings()
{
return m_impl->settings();
}
void session::set_max_uploads(int limit)
{
m_impl->set_max_uploads(limit);
}
void session::set_max_connections(int limit)
{
m_impl->set_max_connections(limit);
}
void session::set_max_half_open_connections(int limit)
{
m_impl->set_max_half_open_connections(limit);
}
void session::set_upload_rate_limit(int bytes_per_second)
{
m_impl->set_upload_rate_limit(bytes_per_second);
}
void session::set_download_rate_limit(int bytes_per_second)
{
m_impl->set_download_rate_limit(bytes_per_second);
}
std::auto_ptr<alert> session::pop_alert()
{
return m_impl->pop_alert();
}
void session::set_severity_level(alert::severity_t s)
{
m_impl->set_severity_level(s);
}
}

1855
cpp/session_impl.cpp Executable file

File diff suppressed because it is too large Load Diff

314
cpp/sha1.cpp Executable file
View File

@@ -0,0 +1,314 @@
/*
SHA-1 C++ conversion
original version:
SHA-1 in C
By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
changelog at the end of the file.
*/
#include <cstdio>
#include <cstring>
// if you don't want boost
// replace with
// #include <stdint.h>
#include <boost/cstdint.hpp>
using boost::uint32_t;
using boost::uint8_t;
#include "libtorrent/config.hpp"
struct TORRENT_EXPORT SHA1_CTX
{
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
};
TORRENT_EXPORT void SHA1Init(SHA1_CTX* context);
TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len);
TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest);
namespace
{
union CHAR64LONG16
{
uint8_t c[64];
uint32_t l[16];
};
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
// blk0() and blk() perform the initial expand.
// I got the idea of expanding during the round function from SSLeay
struct little_endian_blk0
{
static uint32_t apply(CHAR64LONG16* block, int i)
{
return block->l[i] = (rol(block->l[i],24)&0xFF00FF00)
| (rol(block->l[i],8)&0x00FF00FF);
}
};
struct big_endian_blk0
{
static uint32_t apply(CHAR64LONG16* block, int i)
{
return block->l[i];
}
};
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
// (R0+R1), R2, R3, R4 are the different operations used in SHA1
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
// Hash a single 512-bit block. This is the core of the algorithm.
template <class BlkFun>
void SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
{
using namespace std;
uint32_t a, b, c, d, e;
CHAR64LONG16* block;
uint8_t workspace[64];
block = (CHAR64LONG16*)workspace;
memcpy(block, buffer, 64);
// Copy context->state[] to working vars
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
// 4 rounds of 20 operations each. Loop unrolled.
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
// Add the working vars back into context.state[]
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
// Wipe variables
a = b = c = d = e = 0;
}
void SHAPrintContext(SHA1_CTX *context, char *msg)
{
using namespace std;
printf("%s (%d,%d) %x %x %x %x %x\n"
, msg, context->count[0], context->count[1]
, context->state[0], context->state[1]
, context->state[2], context->state[3]
, context->state[4]);
}
template <class BlkFun>
void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
{
using namespace std;
uint32_t i, j; // JHB
#ifdef VERBOSE
SHAPrintContext(context, "before");
#endif
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((j + len) > 63)
{
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform<BlkFun>(context->state, context->buffer);
for ( ; i + 63 < len; i += 64)
{
SHA1Transform<BlkFun>(context->state, &data[i]);
}
j = 0;
}
else
{
i = 0;
}
memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
SHAPrintContext(context, "after ");
#endif
}
bool is_big_endian()
{
uint32_t test = 1;
return *reinterpret_cast<uint8_t*>(&test) == 0;
}
}
// SHA1Init - Initialize new context
void SHA1Init(SHA1_CTX* context)
{
// SHA1 initialization constants
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
// Run your data through this.
void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
{
#if defined __BIG_ENDIAN__
internal_update<big_endian_blk0>(context, data, len);
#elif defined LITTLE_ENDIAN
internal_update<little_endian_blk0>(context, data, len);
#else
// select different functions depending on endianess
// and figure out the endianess runtime
if (is_big_endian())
internal_update<big_endian_blk0>(context, data, len);
else
internal_update<little_endian_blk0>(context, data, len);
#endif
}
// Add padding and return the message digest.
void SHA1Final(SHA1_CTX* context, uint8_t* digest)
{
uint8_t finalcount[8];
for (uint32_t i = 0; i < 8; ++i)
{
// Endian independent
finalcount[i] = static_cast<uint8_t>(
(context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255);
}
SHA1Update(context, (uint8_t const*)"\200", 1);
while ((context->count[0] & 504) != 448)
SHA1Update(context, (uint8_t const*)"\0", 1);
SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform()
for (uint32_t i = 0; i < 20; ++i)
{
digest[i] = static_cast<unsigned char>(
(context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
}
/************************************************************
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
However, on 16 bit machines an int is too small for the shifts being done
against
it. This caused the hash function to generate incorrect values if len was
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
an
int and a long are the same size in those environments.
--
I also corrected a few compiler warnings generated by Borland C.
1. Added #include <process.h> for exit() prototype
2. Removed unused variable 'j' in SHA1Final
3. Changed exit(0) to return(0) at end of main.
ALL changes I made can be located by searching for comments containing 'JHB'
-----------------
Modified 8/98
By Steve Reid <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> and used return() instead of exit()
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-----------------
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Converted to C++ 6/04
By Arvid Norberg <arvidn@sourceforge.net>
1- made the input buffer const, and made the
previous SHA1HANDSOFF implicit
2- uses C99 types with size guarantees
from boost
3- if none of __BIG_ENDIAN__ or LITTLE_ENDIAN
are defined, endianess is determined
at runtime. templates are used to duplicate
the transform function for each endianess
4- using anonymous namespace to avoid external
linkage on internal functions
5- using standard C++ includes
still 100% PD
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/

91
cpp/stat.cpp Executable file
View File

@@ -0,0 +1,91 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: Use two algorithms to estimate transfer rate.
// one (simple) for transfer rates that are >= 1 packet
// per second and one (low pass-filter) for rates < 1
// packet per second.
#include <numeric>
#include "libtorrent/stat.hpp"
#include "libtorrent/invariant_check.hpp"
#include <algorithm>
#if defined _MSC_VER && _MSC_VER <= 1200
#define for if (false) {} else for
#endif
using namespace libtorrent;
void libtorrent::stat::second_tick(float tick_interval)
{
INVARIANT_CHECK;
for (int i = history - 2; i >= 0; --i)
{
m_download_rate_history[i + 1] = m_download_rate_history[i];
m_upload_rate_history[i + 1] = m_upload_rate_history[i];
m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i];
m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i];
}
m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol)
/ tick_interval;
m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol)
/ tick_interval;
m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval;
m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval;
m_downloaded_payload = 0;
m_uploaded_payload = 0;
m_downloaded_protocol = 0;
m_uploaded_protocol = 0;
m_mean_download_rate = 0;
m_mean_upload_rate = 0;
m_mean_download_payload_rate = 0;
m_mean_upload_payload_rate = 0;
for (int i = 0; i < history; ++i)
{
m_mean_download_rate += m_download_rate_history[i];
m_mean_upload_rate += m_upload_rate_history[i];
m_mean_download_payload_rate += m_download_payload_rate_history[i];
m_mean_upload_payload_rate += m_upload_payload_rate_history[i];
}
m_mean_download_rate /= history;
m_mean_upload_rate /= history;
m_mean_download_payload_rate /= history;
m_mean_upload_payload_rate /= history;
}

2172
cpp/storage.cpp Executable file

File diff suppressed because it is too large Load Diff

2186
cpp/torrent.cpp Executable file

File diff suppressed because it is too large Load Diff

729
cpp/torrent_handle.cpp Executable file
View File

@@ -0,0 +1,729 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/invariant_check.hpp"
#if defined(_MSC_VER) && _MSC_VER < 1300
namespace std
{
using ::srand;
using ::isalnum;
};
#endif
using boost::bind;
using boost::mutex;
using libtorrent::aux::session_impl;
namespace libtorrent
{
namespace
{
void throw_invalid_handle()
{
throw invalid_handle();
}
template<class Ret, class F>
Ret call_member(
session_impl* ses
, aux::checker_impl* chk
, sha1_hash const& hash
, F f)
{
if (ses == 0) throw_invalid_handle();
if (chk)
{
mutex::scoped_lock l(chk->m_mutex);
aux::piece_checker_data* d = chk->find_torrent(hash);
if (d != 0) return f(*d->torrent_ptr);
}
{
session_impl::mutex_t::scoped_lock l(ses->m_mutex);
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
if (t) return f(*t);
}
throw invalid_handle();
}
}
#ifndef NDEBUG
void torrent_handle::check_invariant() const
{
assert((m_ses == 0 && m_chk == 0) || (m_ses != 0));
}
#endif
void torrent_handle::set_max_uploads(int max_uploads) const
{
INVARIANT_CHECK;
assert(max_uploads >= 2 || max_uploads == -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_max_uploads, _1, max_uploads));
}
void torrent_handle::use_interface(const char* net_interface) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::use_interface, _1, net_interface));
}
void torrent_handle::set_max_connections(int max_connections) const
{
INVARIANT_CHECK;
assert(max_connections >= 2 || max_connections == -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_max_connections, _1, max_connections));
}
void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_upload_limit, _1, ip, limit));
}
void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_download_limit, _1, ip, limit));
}
void torrent_handle::set_upload_limit(int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_upload_limit, _1, limit));
}
void torrent_handle::set_download_limit(int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_download_limit, _1, limit));
}
bool torrent_handle::move_storage(
boost::filesystem::path const& save_path) const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::move_storage, _1, save_path));
}
bool torrent_handle::has_metadata() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::valid_metadata, _1));
}
bool torrent_handle::is_seed() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_seed, _1));
}
bool torrent_handle::is_paused() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_paused, _1));
}
void torrent_handle::pause() const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::pause, _1));
}
void torrent_handle::resume() const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::resume, _1));
}
void torrent_handle::set_tracker_login(std::string const& name
, std::string const& password) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_tracker_login, _1, name, password));
}
void torrent_handle::file_progress(std::vector<float>& progress)
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
if (!d->processing)
{
torrent_info const& info = d->torrent_ptr->torrent_file();
progress.clear();
progress.resize(info.num_files(), 0.f);
return;
}
d->torrent_ptr->file_progress(progress);
return;
}
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->file_progress(progress);
}
throw_invalid_handle();
}
torrent_status torrent_handle::status() const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
torrent_status st;
if (d->processing)
{
if (d->torrent_ptr->is_allocating())
st.state = torrent_status::allocating;
else
st.state = torrent_status::checking_files;
}
else
st.state = torrent_status::queued_for_checking;
st.progress = d->progress;
st.paused = d->torrent_ptr->is_paused();
return st;
}
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->status();
}
throw_invalid_handle();
return torrent_status();
}
void torrent_handle::set_sequenced_download_threshold(int threshold) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_sequenced_download_threshold, _1, threshold));
}
void torrent_handle::filter_piece(int index, bool filter) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_piece, _1, index, filter));
}
void torrent_handle::filter_pieces(std::vector<bool> const& pieces) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_pieces, _1, pieces));
}
bool torrent_handle::is_piece_filtered(int index) const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_piece_filtered, _1, index));
}
std::vector<bool> torrent_handle::filtered_pieces() const
{
INVARIANT_CHECK;
std::vector<bool> ret;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filtered_pieces, _1, boost::ref(ret)));
return ret;
}
void torrent_handle::filter_files(std::vector<bool> const& files) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_files, _1, files));
}
std::vector<announce_entry> const& torrent_handle::trackers() const
{
INVARIANT_CHECK;
return call_member<std::vector<announce_entry> const&>(m_ses
, m_chk, m_info_hash, bind(&torrent::trackers, _1));
}
void torrent_handle::add_url_seed(std::string const& url)
{
INVARIANT_CHECK;
return call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::add_url_seed, _1, url));
}
void torrent_handle::replace_trackers(
std::vector<announce_entry> const& urls) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::replace_trackers, _1, urls));
}
const torrent_info& torrent_handle::get_torrent_info() const
{
INVARIANT_CHECK;
if (!has_metadata()) throw_invalid_handle();
return call_member<torrent_info const&>(m_ses, m_chk, m_info_hash
, bind(&torrent::torrent_file, _1));
}
bool torrent_handle::is_valid() const
{
INVARIANT_CHECK;
if (m_ses == 0) return false;
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0) return true;
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
if (!t.expired()) return true;
}
return false;
}
entry torrent_handle::write_resume_data() const
{
INVARIANT_CHECK;
std::vector<int> piece_index;
if (m_ses == 0) return entry();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return entry();
if (!t->valid_metadata()) return entry();
t->filesystem().export_piece_map(piece_index);
entry ret(entry::dictionary_t);
ret["file-format"] = "libtorrent resume file";
ret["file-version"] = 1;
const sha1_hash& info_hash = t->torrent_file().info_hash();
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
ret["slots"] = entry(entry::list_t);
entry::list_type& slots = ret["slots"].list();
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
const piece_picker& p = t->picker();
const std::vector<piece_picker::downloading_piece>& q
= p.get_download_queue();
// blocks per piece
int num_blocks_per_piece =
static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
ret["blocks per piece"] = num_blocks_per_piece;
// unfinished pieces
ret["unfinished"] = entry::list_type();
entry::list_type& up = ret["unfinished"].list();
// info for each unfinished piece
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin(); i != q.end(); ++i)
{
if (i->finished_blocks.count() == 0) continue;
entry piece_struct(entry::dictionary_t);
// the unfinished piece's index
piece_struct["piece"] = i->index;
std::string bitmask;
const int num_bitmask_bytes
= std::max(num_blocks_per_piece / 8, 1);
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char v = 0;
for (int k = 0; k < 8; ++k)
v |= i->finished_blocks[j*8+k]?(1 << k):0;
bitmask.insert(bitmask.end(), v);
}
piece_struct["bitmask"] = bitmask;
assert(t->filesystem().slot_for_piece(i->index) >= 0);
unsigned long adler
= t->filesystem().piece_crc(
t->filesystem().slot_for_piece(i->index)
, t->block_size()
, i->finished_blocks);
piece_struct["adler32"] = adler;
// push the struct onto the unfinished-piece list
up.push_back(piece_struct);
}
// write local peers
ret["peers"] = entry::list_type();
entry::list_type& peer_list = ret["peers"].list();
policy& pol = t->get_policy();
for (policy::iterator i = pol.begin_peer()
, end(pol.end_peer()); i != end; ++i)
{
// we cannot save remote connection
// since we don't know their listen port
// unless they gave us their listen port
// through the extension handshake
// so, if the peer is not connectable (i.e. we
// don't know its listen port) or if it has
// been banned, don't save it.
if (i->type == policy::peer::not_connectable
|| i->banned) continue;
tcp::endpoint ip = i->ip;
entry peer(entry::dictionary_t);
peer["ip"] = ip.address().to_string();
peer["port"] = ip.port();
peer_list.push_back(peer);
}
std::vector<std::pair<size_type, std::time_t> > file_sizes
= get_filesizes(t->torrent_file(), t->save_path());
ret["file sizes"] = entry::list_type();
entry::list_type& fl = ret["file sizes"].list();
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
{
entry::list_type p;
p.push_back(entry(i->first));
p.push_back(entry(i->second));
fl.push_back(entry(p));
}
return ret;
}
boost::filesystem::path torrent_handle::save_path() const
{
INVARIANT_CHECK;
return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
, bind(&torrent::save_path, _1));
}
std::vector<char> const& torrent_handle::metadata() const
{
INVARIANT_CHECK;
return call_member<std::vector<char> const&>(m_ses, m_chk, m_info_hash
, bind(&torrent::metadata, _1));
}
void torrent_handle::connect_peer(tcp::endpoint const& adr) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t)
{
// the torrent is being checked. Add the peer to its
// peer list. The entries in there will be connected
// once the checking is complete.
mutex::scoped_lock l2(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d == 0) throw_invalid_handle();
d->peers.push_back(adr);
return;
}
peer_id id;
std::fill(id.begin(), id.end(), 0);
t->get_policy().peer_from_tracker(adr, id);
}
void torrent_handle::force_reannounce(
boost::posix_time::time_duration duration) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
using boost::posix_time::second_clock;
t->force_tracker_request(second_clock::universal_time()
+ duration);
}
void torrent_handle::force_reannounce() const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
t->force_tracker_request();
}
void torrent_handle::set_ratio(float ratio) const
{
INVARIANT_CHECK;
assert(ratio >= 0.f);
if (ratio < 1.f && ratio > 0.f)
ratio = 1.f;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_ratio, _1, ratio));
}
void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
{
INVARIANT_CHECK;
v.clear();
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
{
peer_connection* peer = i->second;
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent().expired()) continue;
v.push_back(peer_info());
peer_info& p = v.back();
peer->get_peer_info(p);
}
}
bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const
{
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return false;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
{
peer_connection* peer = i->second;
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent().expired()) continue;
tcp::endpoint sender = peer->get_socket()->remote_endpoint();
// loop until we find the required ip tcp::endpoint
if (ip != sender) continue;
bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
if (!p) return false;
// peers that don's support chat message extension
// should not be included either
if (!p->supports_extension(extended_chat_message))
return false;
// send the message
p->write_chat_message(message);
return true;
}
return false;
}
void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
queue.clear();
if (!t) return;
if (!t->valid_metadata()) return;
const piece_picker& p = t->picker();
const std::vector<piece_picker::downloading_piece>& q
= p.get_download_queue();
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin(); i != q.end(); ++i)
{
partial_piece_info pi;
pi.finished_blocks = i->finished_blocks;
pi.requested_blocks = i->requested_blocks;
for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
{
pi.peer[j] = i->info[j].peer;
pi.num_downloads[j] = i->info[j].num_downloads;
}
pi.piece_index = i->index;
pi.blocks_in_piece = p.blocks_in_piece(i->index);
queue.push_back(pi);
}
}
}

833
cpp/torrent_info.cpp Executable file
View File

@@ -0,0 +1,833 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/next_prior.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
using namespace libtorrent;
using namespace boost::filesystem;
namespace
{
void convert_to_utf8(std::string& str, unsigned char chr)
{
str += 0xc0 | ((chr & 0xff) >> 6);
str += 0x80 | (chr & 0x3f);
}
void verify_encoding(file_entry& target)
{
std::string tmp_path;
std::string file_path = target.path.string();
bool valid_encoding = true;
for (std::string::iterator i = file_path.begin()
, end(file_path.end()); i != end; ++i)
{
// valid ascii-character
if ((*i & 0x80) == 0)
{
tmp_path += *i;
continue;
}
if (std::distance(i, end) < 2)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 2-byte utf-8 character
if ((i[0] & 0xe0) == 0xc0
&& (i[1] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
i += 1;
continue;
}
if (std::distance(i, end) < 3)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 3-byte utf-8 character
if ((i[0] & 0xf0) == 0xe0
&& (i[1] & 0xc0) == 0x80
&& (i[2] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
tmp_path += i[2];
i += 2;
continue;
}
if (std::distance(i, end) < 4)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 4-byte utf-8 character
if ((i[0] & 0xf0) == 0xe0
&& (i[1] & 0xc0) == 0x80
&& (i[2] & 0xc0) == 0x80
&& (i[3] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
tmp_path += i[2];
tmp_path += i[3];
i += 3;
continue;
}
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
}
// the encoding was not valid utf-8
// save the original encoding and replace the
// commonly used path with the correctly
// encoded string
if (!valid_encoding)
{
target.orig_path.reset(new path(target.path));
target.path = tmp_path;
}
}
void extract_single_file(const entry& dict, file_entry& target
, std::string const& root_dir)
{
target.size = dict["length"].integer();
target.path = root_dir;
// prefer the name.utf-8
// because if it exists, it is more
// likely to be correctly encoded
const entry::list_type* list = 0;
if (entry const* p = dict.find_key("path.utf-8"))
{
list = &p->list();
}
else
{
list = &dict["path"].list();
}
for (entry::list_type::const_iterator i = list->begin();
i != list->end(); ++i)
{
if (i->string() != "..")
target.path /= i->string();
}
verify_encoding(target);
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '"
+ target.path.native_file_string() + "'");
}
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
, std::string const& root_dir)
{
size_type offset = 0;
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
{
target.push_back(file_entry());
extract_single_file(*i, target.back(), root_dir);
target.back().offset = offset;
offset += target.back().size;
}
}
void remove_dir(path& p)
{
assert(p.begin() != p.end());
path tmp;
for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
tmp /= *i;
p = tmp;
}
}
namespace libtorrent
{
using namespace boost::gregorian;
using namespace boost::posix_time;
// standard constructor that parses a torrent file
torrent_info::torrent_info(const entry& torrent_file)
: m_creation_date(date(not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_extra_info(entry::dictionary_t)
{
try
{
read_torrent_info(torrent_file);
}
catch(type_error&)
{
throw invalid_torrent_file();
}
}
// constructor used for creating new torrents
// will not contain any hashes, comments, creation date
// just the necessary to use it with piece manager
// used for torrents with no metadata
torrent_info::torrent_info(sha1_hash const& info_hash)
: m_piece_length(256 * 1024)
, m_total_size(0)
, m_info_hash(info_hash)
, m_name()
, m_creation_date(second_clock::universal_time())
, m_multifile(false)
, m_extra_info(entry::dictionary_t)
{
}
torrent_info::torrent_info()
: m_piece_length(256 * 1024)
, m_total_size(0)
, m_info_hash(0)
, m_name()
, m_creation_date(second_clock::universal_time())
, m_multifile(false)
, m_extra_info(entry::dictionary_t)
{
}
torrent_info::~torrent_info()
{}
void torrent_info::set_piece_size(int size)
{
// make sure the size is an even power of 2
#ifndef NDEBUG
for (int i = 0; i < 32; ++i)
{
if (size & (1 << i))
{
assert((size & ~(1 << i)) == 0);
break;
}
}
#endif
m_piece_length = size;
int num_pieces = static_cast<int>(
(m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = static_cast<int>(m_piece_hash.size());
m_piece_hash.resize(num_pieces);
for (int i = old_num_pieces; i < num_pieces; ++i)
{
m_piece_hash[i].clear();
}
}
void torrent_info::parse_info_section(entry const& info)
{
// encode the info-field in order to calculate it's sha1-hash
std::vector<char> buf;
bencode(std::back_inserter(buf), info);
hasher h;
h.update(&buf[0], (int)buf.size());
m_info_hash = h.final();
// extract piece length
m_piece_length = (int)info["piece length"].integer();
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
// extract file name (or the directory name if it's a multifile libtorrent)
if (entry const* e = info.find_key("name.utf-8"))
{ m_name = e->string(); }
else
{ m_name = info["name"].string(); }
path tmp = m_name;
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" + m_name + "'");
if (tmp.has_branch_path()) throw std::runtime_error(
"torrent contains name with directories: '" + m_name + "'");
// extract file list
entry const* i = info.find_key("files");
if (i == 0)
{
// if there's no list of files, there has to be a length
// field.
file_entry e;
e.path = m_name;
e.offset = 0;
e.size = info["length"].integer();
m_files.push_back(e);
}
else
{
extract_files(i->list(), m_files, m_name);
m_multifile = true;
}
// calculate total size of all pieces
m_total_size = 0;
for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
m_total_size += i->size;
// extract sha-1 hashes for all pieces
// we want this division to round upwards, that's why we have the
// extra addition
int num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
m_piece_hash.resize(num_pieces);
const std::string& hash_string = info["pieces"].string();
if ((int)hash_string.length() != num_pieces * 20)
throw invalid_torrent_file();
for (int i = 0; i < num_pieces; ++i)
std::copy(
hash_string.begin() + i*20
, hash_string.begin() + (i+1)*20
, m_piece_hash[i].begin());
for (entry::dictionary_type::const_iterator i = info.dict().begin()
, end(info.dict().end()); i != end; ++i)
{
if (i->first == "pieces"
|| i->first == "piece length"
|| i->first == "length")
continue;
m_extra_info[i->first] = i->second;
}
if (entry const* priv = info.find_key("private"))
{
if (priv->type() != entry::int_t
|| priv->integer() != 0)
{
// this key exists and it's not 0.
// consider the torrent private
m_private = true;
}
}
#ifndef NDEBUG
std::vector<char> info_section_buf;
entry gen_info_section = create_info_metadata();
bencode(std::back_inserter(info_section_buf), gen_info_section);
assert(hasher(&info_section_buf[0], info_section_buf.size()).final()
== m_info_hash);
#endif
}
// extracts information from a libtorrent file and fills in the structures in
// the torrent object
void torrent_info::read_torrent_info(const entry& torrent_file)
{
// extract the url of the tracker
if (entry const* i = torrent_file.find_key("announce-list"))
{
const entry::list_type& l = i->list();
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
{
const entry::list_type& ll = j->list();
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
{
announce_entry e(k->string());
e.tier = (int)std::distance(l.begin(), j);
m_urls.push_back(e);
}
}
if (m_urls.size() == 0)
{
// the announce-list is empty
// fall back to look for announce
m_urls.push_back(announce_entry(
torrent_file["announce"].string()));
}
// shuffle each tier
std::vector<announce_entry>::iterator start = m_urls.begin();
std::vector<announce_entry>::iterator stop;
int current_tier = m_urls.front().tier;
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
{
if (stop->tier != current_tier)
{
std::random_shuffle(start, stop);
start = stop;
current_tier = stop->tier;
}
}
std::random_shuffle(start, stop);
}
else if (entry const* i = torrent_file.find_key("announce"))
{
m_urls.push_back(announce_entry(i->string()));
}
if (entry const* i = torrent_file.find_key("nodes"))
{
entry::list_type const& list = i->list();
for (entry::list_type::const_iterator i(list.begin())
, end(list.end()); i != end; ++i)
{
if (i->type() != entry::list_t) continue;
entry::list_type const& l = i->list();
entry::list_type::const_iterator iter = l.begin();
if (l.size() < 1) continue;
std::string const& hostname = iter->string();
++iter;
int port = 6881;
if (l.end() != iter) port = iter->integer();
m_nodes.push_back(std::make_pair(hostname, port));
}
}
// extract creation date
try
{
m_creation_date = ptime(date(1970, Jan, 1))
+ seconds(long(torrent_file["creation date"].integer()));
}
catch (type_error) {}
// if there are any url-seeds, extract them
try
{
entry const& url_seeds = torrent_file["url-list"];
if (url_seeds.type() == entry::string_t)
{
m_url_seeds.push_back(url_seeds.string());
}
else if (url_seeds.type() == entry::list_t)
{
entry::list_type const& l = url_seeds.list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
{
m_url_seeds.push_back(i->string());
}
}
}
catch (type_error&) {}
// extract comment
if (entry const* e = torrent_file.find_key("comment.utf-8"))
{ m_comment = e->string(); }
else if (entry const* e = torrent_file.find_key("comment"))
{ m_comment = e->string(); }
if (entry const* e = torrent_file.find_key("created by.utf-8"))
{ m_created_by = e->string(); }
else if (entry const* e = torrent_file.find_key("created by"))
{ m_created_by = e->string(); }
parse_info_section(torrent_file["info"]);
}
boost::optional<ptime>
torrent_info::creation_date() const
{
if (m_creation_date != ptime(date(not_a_date_time)))
{
return boost::optional<ptime>(m_creation_date);
}
return boost::optional<ptime>();
}
void torrent_info::add_tracker(std::string const& url, int tier)
{
announce_entry e(url);
e.tier = tier;
m_urls.push_back(e);
using boost::bind;
std::sort(m_urls.begin(), m_urls.end(), boost::bind<bool>(std::less<int>()
, bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
}
void torrent_info::add_file(boost::filesystem::path file, size_type size)
{
assert(file.begin() != file.end());
if (!file.has_branch_path())
{
// you have already added at least one file with a
// path to the file (branch_path), which means that
// all the other files need to be in the same top
// directory as the first file.
assert(m_files.empty());
assert(!m_multifile);
m_name = file.string();
}
else
{
#ifndef NDEBUG
if (!m_files.empty())
assert(m_name == *file.begin());
#endif
m_multifile = true;
m_name = *file.begin();
}
file_entry e;
e.path = file;
e.size = size;
m_files.push_back(e);
m_total_size += size;
int num_pieces = static_cast<int>(
(m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = static_cast<int>(m_piece_hash.size());
m_piece_hash.resize(num_pieces);
for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
i != m_piece_hash.end(); ++i)
{
i->clear();
}
}
void torrent_info::add_url_seed(std::string const& url)
{
m_url_seeds.push_back(url);
}
void torrent_info::set_comment(char const* str)
{
m_comment = str;
}
void torrent_info::set_creator(char const* str)
{
m_created_by = str;
}
entry torrent_info::create_info_metadata() const
{
namespace fs = boost::filesystem;
// you have to add files to the torrent first
assert(!m_files.empty());
entry info(m_extra_info);
if (!info.find_key("name"))
info["name"] = m_name;
if (!m_multifile)
{
info["length"] = m_files.front().size;
}
else
{
if (!info.find_key("files"))
{
entry& files = info["files"];
files = entry(entry::list_t);
for (std::vector<file_entry>::const_iterator i = m_files.begin();
i != m_files.end(); ++i)
{
files.list().push_back(entry(entry::dictionary_t));
entry& file_e = files.list().back();
file_e["length"] = i->size;
entry& path_e = file_e["path"];
path_e = entry(entry::list_t);
fs::path const* file_path;
if (i->orig_path) file_path = &(*i->orig_path);
else file_path = &i->path;
assert(file_path->has_branch_path());
assert(*file_path->begin() == m_name);
for (fs::path::iterator j = boost::next(file_path->begin());
j != file_path->end(); ++j)
{
path_e.list().push_back(entry(*j));
}
}
}
}
info["piece length"] = piece_length();
entry& pieces = info["pieces"];
pieces = entry(entry::string_t);
std::string& p = pieces.string();
for (std::vector<sha1_hash>::const_iterator i = m_piece_hash.begin();
i != m_piece_hash.end(); ++i)
{
p.append((char*)i->begin(), (char*)i->end());
}
return info;
}
entry torrent_info::create_torrent() const
{
assert(m_piece_length > 0);
using namespace boost::gregorian;
using namespace boost::posix_time;
namespace fs = boost::filesystem;
entry dict(entry::dictionary_t);
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
{
// TODO: throw something here
// throw
return entry();
}
if (m_private) dict["private"] = 1;
if (!m_urls.empty())
dict["announce"] = m_urls.front().url;
if (!m_nodes.empty())
{
entry& nodes = dict["nodes"];
nodes = entry(entry::list_t);
entry::list_type& nodes_list = nodes.list();
for (nodes_t::const_iterator i = m_nodes.begin()
, end(m_nodes.end()); i != end; ++i)
{
entry::list_type node;
node.push_back(entry(i->first));
node.push_back(entry(i->second));
nodes_list.push_back(entry(node));
}
}
if (m_urls.size() > 1)
{
entry trackers(entry::list_t);
entry tier(entry::list_t);
int current_tier = m_urls.front().tier;
for (std::vector<announce_entry>::const_iterator i = m_urls.begin();
i != m_urls.end(); ++i)
{
if (i->tier != current_tier)
{
current_tier = i->tier;
trackers.list().push_back(tier);
tier.list().clear();
}
tier.list().push_back(entry(i->url));
}
trackers.list().push_back(tier);
dict["announce-list"] = trackers;
}
if (!m_comment.empty())
dict["comment"] = m_comment;
dict["creation date"] =
(m_creation_date - ptime(date(1970, Jan, 1))).total_seconds();
if (!m_created_by.empty())
dict["created by"] = m_created_by;
if (!m_url_seeds.empty())
{
if (m_url_seeds.size() == 1)
{
dict["url-list"] = m_url_seeds.front();
}
else
{
entry& list = dict["url-list"];
list = entry(entry::list_t);
for (std::vector<std::string>::const_iterator i
= m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
{
list.list().push_back(entry(*i));
}
}
}
dict["info"] = create_info_metadata();
entry const& info_section = dict["info"];
std::vector<char> buf;
bencode(std::back_inserter(buf), info_section);
m_info_hash = hasher(&buf[0], buf.size()).final();
return dict;
}
void torrent_info::set_hash(int index, const sha1_hash& h)
{
assert(index >= 0);
assert(index < (int)m_piece_hash.size());
m_piece_hash[index] = h;
}
void torrent_info::convert_file_names()
{
assert(false);
}
void torrent_info::print(std::ostream& os) const
{
os << "trackers:\n";
for (std::vector<announce_entry>::const_iterator i = trackers().begin();
i != trackers().end(); ++i)
{
os << i->tier << ": " << i->url << "\n";
}
if (!m_comment.empty())
os << "comment: " << m_comment << "\n";
if (m_creation_date != ptime(date(not_a_date_time)))
os << "creation date: " << to_simple_string(m_creation_date) << "\n";
os << "private: " << (m_private?"yes":"no") << "\n";
os << "number of pieces: " << num_pieces() << "\n";
os << "piece length: " << piece_length() << "\n";
os << "files:\n";
for (file_iterator i = begin_files(); i != end_files(); ++i)
os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
}
size_type torrent_info::piece_size(int index) const
{
assert(index >= 0 && index < num_pieces());
if (index == num_pieces()-1)
{
size_type size = total_size()
- (num_pieces() - 1) * piece_length();
assert(size > 0);
assert(size <= piece_length());
return size;
}
else
return piece_length();
}
void torrent_info::add_node(std::pair<std::string, int> const& node)
{
m_nodes.push_back(node);
}
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
, int size) const
{
assert(num_files() > 0);
std::vector<file_slice> ret;
size_type start = piece * (size_type)m_piece_length + offset;
assert(start + size <= m_total_size);
// find the file iterator and file offset
// TODO: make a vector that can map piece -> file index in O(1)
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
int counter = 0;
for (file_iter = begin_files();; ++counter, ++file_iter)
{
assert(file_iter != end_files());
if (file_offset < file_iter->size)
{
file_slice f;
f.file_index = counter;
f.offset = file_offset;
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size;
file_offset += f.size;
ret.push_back(f);
}
assert(size >= 0);
if (size <= 0) break;
file_offset -= file_iter->size;
}
return ret;
}
peer_request torrent_info::map_file(int file_index, size_type file_offset
, int size) const
{
assert(file_index < (int)m_files.size());
assert(file_index >= 0);
size_type offset = file_offset + m_files[file_index].offset;
peer_request ret;
ret.piece = offset / piece_length();
ret.start = offset - ret.piece * piece_length();
ret.length = size;
return ret;
}
}

569
cpp/tracker_manager.cpp Executable file
View File

@@ -0,0 +1,569 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#include <boost/bind.hpp>
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
using namespace libtorrent;
using boost::tuples::make_tuple;
using boost::tuples::tuple;
using boost::bind;
namespace
{
enum
{
minimum_tracker_response_length = 3,
http_buffer_size = 2048
};
enum
{
FTEXT = 0x01,
FHCRC = 0x02,
FEXTRA = 0x04,
FNAME = 0x08,
FCOMMENT = 0x10,
FRESERVED = 0xe0,
GZIP_MAGIC0 = 0x1f,
GZIP_MAGIC1 = 0x8b
};
}
namespace libtorrent
{
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
// returns -1 if gzip header is invalid or the header size in bytes
int gzip_header(const char* buf, int size)
{
assert(buf != 0);
assert(size > 0);
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
const int total_size = size;
// The zip header cannot be shorter than 10 bytes
if (size < 10) return -1;
// check the magic header of gzip
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
int method = buffer[2];
int flags = buffer[3];
// check for reserved flag and make sure it's compressed with the correct metod
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
// skip time, xflags, OS code
size -= 10;
buffer += 10;
if (flags & FEXTRA)
{
int extra_len;
if (size < 2) return -1;
extra_len = (buffer[1] << 8) | buffer[0];
if (size < (extra_len+2)) return -1;
size -= (extra_len + 2);
buffer += (extra_len + 2);
}
if (flags & FNAME)
{
while (size && *buffer)
{
--size;
++buffer;
}
if (!size || *buffer) return -1;
--size;
++buffer;
}
if (flags & FCOMMENT)
{
while (size && *buffer)
{
--size;
++buffer;
}
if (!size || *buffer) return -1;
--size;
++buffer;
}
if (flags & FHCRC)
{
if (size < 2) return -1;
size -= 2;
buffer += 2;
}
return total_size - size;
}
bool inflate_gzip(
std::vector<char>& buffer
, tracker_request const& req
, request_callback* requester
, int maximum_tracker_response_length)
{
assert(maximum_tracker_response_length > 0);
int header_len = gzip_header(&buffer[0], (int)buffer.size());
if (header_len < 0)
{
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
return true;
}
// start off wth one kilobyte and grow
// if needed
std::vector<char> inflate_buffer(1024);
// initialize the zlib-stream
z_stream str;
// subtract 8 from the end of the buffer since that's CRC32 and input size
// and those belong to the gzip file
str.avail_in = (int)buffer.size() - header_len - 8;
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
str.avail_out = (int)inflate_buffer.size();
str.zalloc = Z_NULL;
str.zfree = Z_NULL;
str.opaque = 0;
// -15 is really important. It will make inflate() not look for a zlib header
// and just deflate the buffer
if (inflateInit2(&str, -15) != Z_OK)
{
requester->tracker_request_error(req, 200, "gzip out of memory");
return true;
}
// inflate and grow inflate_buffer as needed
int ret = inflate(&str, Z_SYNC_FLUSH);
while (ret == Z_OK)
{
if (str.avail_out == 0)
{
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
{
inflateEnd(&str);
requester->tracker_request_error(req, 200
, "tracker response too large");
return true;
}
int new_size = (int)inflate_buffer.size() * 2;
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
int old_size = (int)inflate_buffer.size();
inflate_buffer.resize(new_size);
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
str.avail_out = new_size - old_size;
}
ret = inflate(&str, Z_SYNC_FLUSH);
}
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
inflateEnd(&str);
if (ret != Z_STREAM_END)
{
requester->tracker_request_error(req, 200, "gzip error");
return true;
}
// commit the resulting buffer
std::swap(buffer, inflate_buffer);
return false;
}
std::string base64encode(const std::string& s)
{
static const char base64_table[] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
unsigned char inbuf[3];
unsigned char outbuf[4];
std::string ret;
for (std::string::const_iterator i = s.begin(); i != s.end();)
{
// available input is 1,2 or 3 bytes
// since we read 3 bytes at a time at most
int available_input = std::min(3, (int)std::distance(i, s.end()));
// clear input buffer
std::fill(inbuf, inbuf+3, 0);
// read a chunk of input into inbuf
for (int j = 0; j < available_input; ++j)
{
inbuf[j] = *i;
++i;
}
// encode inbuf to outbuf
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
outbuf[3] = inbuf[2] & 0x3f;
// write output
for (int j = 0; j < available_input+1; ++j)
{
ret += base64_table[outbuf[j]];
}
// write pad
for (int j = 0; j < 3 - available_input; ++j)
{
ret += '=';
}
}
return ret;
}
void intrusive_ptr_add_ref(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs >= 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
++c->m_refs;
}
void intrusive_ptr_release(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs > 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
--c->m_refs;
if (c->m_refs == 0)
{
l.unlock();
delete c;
}
}
timeout_handler::timeout_handler(demuxer& d)
: m_demuxer(d)
, m_start_time(second_clock::universal_time())
, m_read_time(second_clock::universal_time())
, m_timeout(d)
, m_completion_timeout(0)
, m_read_timeout(0)
, m_refs(0)
{}
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
{
m_completion_timeout = completion_timeout;
m_read_timeout = read_timeout;
m_start_time = second_clock::universal_time();
m_read_time = second_clock::universal_time();
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
void timeout_handler::restart_read_timeout()
{
m_read_time = second_clock::universal_time();
}
void timeout_handler::cancel()
{
m_completion_timeout = 0;
m_timeout.cancel();
}
void timeout_handler::timeout_callback(asio::error const& error) try
{
if (error) return;
if (m_completion_timeout == 0) return;
ptime now(second_clock::universal_time());
time_duration receive_timeout = now - m_read_time;
time_duration completion_timeout = now - m_start_time;
if (m_read_timeout
< receive_timeout.total_seconds()
|| m_completion_timeout
< completion_timeout.total_seconds())
{
on_timeout();
return;
}
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
catch (std::exception& e)
{
assert(false);
}
tracker_connection::tracker_connection(
tracker_manager& man
, tracker_request req
, demuxer& d
, boost::weak_ptr<request_callback> r)
: timeout_handler(d)
, m_requester(r)
, m_man(man)
, m_req(req)
{}
request_callback& tracker_connection::requester()
{
boost::shared_ptr<request_callback> r = m_requester.lock();
assert(r);
return *r;
}
void tracker_connection::fail(int code, char const* msg)
{
if (has_requester()) requester().tracker_request_error(
m_req, code, msg);
close();
}
void tracker_connection::fail_timeout()
{
if (has_requester()) requester().tracker_request_timed_out(m_req);
close();
}
void tracker_connection::close()
{
cancel();
m_man.remove_request(this);
}
void tracker_manager::remove_request(tracker_connection const* c)
{
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t::iterator i = std::find(m_connections.begin()
, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
if (i == m_connections.end()) return;
m_connections.erase(i);
}
tuple<std::string, std::string, int, std::string>
parse_url_components(std::string url)
{
std::string hostname; // hostname only
std::string protocol; // should be http
int port = 80;
// PARSE URL
std::string::iterator start = url.begin();
// remove white spaces in front of the url
while (start != url.end() && (*start == ' ' || *start == '\t'))
++start;
std::string::iterator end
= std::find(url.begin(), url.end(), ':');
protocol = std::string(start, end);
if (end == url.end()) throw std::runtime_error("invalid url");
++end;
if (end == url.end()) throw std::runtime_error("invalid url");
if (*end != '/') throw std::runtime_error("invalid url");
++end;
if (end == url.end()) throw std::runtime_error("invalid url");
if (*end != '/') throw std::runtime_error("invalid url");
++end;
start = end;
end = std::find(start, url.end(), '/');
std::string::iterator port_pos
= std::find(start, url.end(), ':');
if (port_pos < end)
{
hostname.assign(start, port_pos);
++port_pos;
try
{
port = boost::lexical_cast<int>(std::string(port_pos, end));
}
catch(boost::bad_lexical_cast&)
{
throw std::runtime_error("invalid url: \"" + url
+ "\", port number expected");
}
}
else
{
hostname.assign(start, end);
}
start = end;
return make_tuple(protocol, hostname, port
, std::string(start, url.end()));
}
void tracker_manager::queue_request(
demuxer& d
, tracker_request req
, std::string const& auth
, boost::weak_ptr<request_callback> c)
{
mutex_t::scoped_lock l(m_mutex);
assert(req.num_want >= 0);
if (req.event == tracker_request::stopped)
req.num_want = 0;
try
{
std::string protocol;
std::string hostname;
int port;
std::string request_string;
boost::tie(protocol, hostname, port, request_string)
= parse_url_components(req.url);
boost::intrusive_ptr<tracker_connection> con;
if (protocol == "http")
{
con = new http_tracker_connection(
d
, *this
, req
, hostname
, port
, request_string
, c
, m_settings
, auth);
}
else if (protocol == "udp")
{
con = new udp_tracker_connection(
d
, *this
, req
, hostname
, port
, c
, m_settings);
}
else
{
throw std::runtime_error("unkown protocol in tracker url");
}
m_connections.push_back(con);
if (con->has_requester()) con->requester().m_manager = this;
}
catch (std::exception& e)
{
if (boost::shared_ptr<request_callback> r = c.lock())
r->tracker_request_error(req, -1, e.what());
}
}
void tracker_manager::abort_all_requests()
{
// removes all connections from m_connections
// except those with a requester == 0 (since those are
// 'event=stopped'-requests)
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t keep_connections;
for (tracker_connections_t::const_iterator i =
m_connections.begin(); i != m_connections.end(); ++i)
{
tracker_request const& req = (*i)->tracker_req();
if (req.event == tracker_request::stopped)
keep_connections.push_back(*i);
}
std::swap(m_connections, keep_connections);
}
bool tracker_manager::empty() const
{
mutex_t::scoped_lock l(m_mutex);
return m_connections.empty();
}
}

522
cpp/udp_tracker_connection.cpp Executable file
View File

@@ -0,0 +1,522 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"
namespace
{
enum
{
udp_connection_retries = 4,
udp_announce_retries = 15,
udp_connect_timeout = 15,
udp_announce_timeout = 10,
udp_buffer_size = 2048
};
}
using namespace boost::posix_time;
using boost::bind;
using boost::lexical_cast;
namespace libtorrent
{
udp_tracker_connection::udp_tracker_connection(
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, boost::weak_ptr<request_callback> c
, session_settings const& stn)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_name_lookup(d)
, m_port(port)
, m_transaction_id(0)
, m_connection_id(0)
, m_settings(stn)
, m_attempts(0)
{
m_socket.reset(new datagram_socket(d));
tcp::resolver::query q(hostname, "0");
m_name_lookup.async_resolve(q
, boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
void udp_tracker_connection::name_lookup(asio::error const& error
, tcp::resolver::iterator i) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error || i == tcp::resolver::iterator())
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("udp tracker name lookup successful");
#endif
restart_read_timeout();
m_target = udp::endpoint(i->endpoint().address(), m_port);
if (has_requester()) requester().m_tracker_address
= tcp::endpoint(i->endpoint().address(), m_port);
m_socket->connect(m_target);
send_udp_connect();
}
catch (std::exception& e)
{
fail(-1, e.what());
};
void udp_tracker_connection::on_timeout()
{
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void udp_tracker_connection::send_udp_connect()
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_CONNECT ["
+ lexical_cast<std::string>(tracker_req().info_hash) + "]");
}
#endif
if (!m_socket) return; // the operation was aborted
char send_buf[16];
char* ptr = send_buf;
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
// connection_id
detail::write_uint32(0x417, ptr);
detail::write_uint32(0x27101980, ptr);
// action (connect)
detail::write_int32(action_connect, ptr);
// transaction_id
detail::write_int32(m_transaction_id, ptr);
m_socket->send(asio::buffer((void*)send_buf, 16), 0);
++m_attempts;
m_buffer.resize(udp_buffer_size);
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
}
void udp_tracker_connection::connect_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
const char* ptr = &m_buffer[0];
int action = detail::read_int32(ptr);
int transaction = detail::read_int32(ptr);
if (action == action_error)
{
fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
return;
}
if (action != action_connect)
{
fail(-1, "invalid action in connect reply");
return;
}
if (m_transaction_id != transaction)
{
fail(-1, "incorrect transaction id");
return;
}
if (bytes_transferred < 16)
{
fail(-1, "udp_tracker_connection: "
"got a message with size < 16");
return;
}
// reset transaction
m_transaction_id = 0;
m_attempts = 0;
m_connection_id = detail::read_int64(ptr);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
+ lexical_cast<std::string>(m_connection_id) + "]");
}
#endif
if (tracker_req().kind == tracker_request::announce_request)
send_udp_announce();
else if (tracker_req().kind == tracker_request::scrape_request)
send_udp_scrape();
}
catch (std::exception& e)
{
fail(-1, e.what());
}
void udp_tracker_connection::send_udp_announce()
{
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
if (!m_socket) return; // the operation was aborted
std::vector<char> buf;
std::back_insert_iterator<std::vector<char> > out(buf);
tracker_request const& req = tracker_req();
// connection_id
detail::write_int64(m_connection_id, out);
// action (announce)
detail::write_int32(action_announce, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(req.info_hash.begin(), req.info_hash.end(), out);
// peer_id
std::copy(req.pid.begin(), req.pid.end(), out);
// downloaded
detail::write_int64(req.downloaded, out);
// left
detail::write_int64(req.left, out);
// uploaded
detail::write_int64(req.uploaded, out);
// event
detail::write_int32(req.event, out);
// ip address
detail::write_int32(0, out);
// key
detail::write_int32(req.key, out);
// num_want
detail::write_int32(req.num_want, out);
// port
detail::write_uint16(req.listen_port, out);
// extensions
detail::write_uint16(0, out);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_ANNOUNCE ["
+ lexical_cast<std::string>(req.info_hash) + "]");
}
#endif
m_socket->send(asio::buffer(buf), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::announce_response, self(), _1, _2));
}
void udp_tracker_connection::send_udp_scrape()
{
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
if (!m_socket) return; // the operation was aborted
std::vector<char> buf;
std::back_insert_iterator<std::vector<char> > out(buf);
// connection_id
detail::write_int64(m_connection_id, out);
// action (scrape)
detail::write_int32(action_scrape, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
m_socket->send(asio::buffer(&buf[0], buf.size()), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
}
void udp_tracker_connection::announce_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
fail(-1, "incorrect transaction id");
return;
}
if (action == action_error)
{
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != action_announce)
{
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int interval = detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
int complete = detail::read_int32(buf);
int num_peers = (bytes_transferred - 20) / 6;
if ((bytes_transferred - 20) % 6 != 0)
{
fail(-1, "invalid udp tracker response length");
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
}
#endif
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
for (int i = 0; i < num_peers; ++i)
{
peer_entry e;
std::stringstream s;
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf);
e.ip = s.str();
e.port = detail::read_uint16(buf);
e.pid.clear();
peer_list.push_back(e);
}
requester().tracker_response(tracker_req(), peer_list, interval
, complete, incomplete);
m_man.remove_request(this);
return;
}
catch (std::exception& e)
{
fail(-1, e.what());
}; // msvc 7.1 seems to require this
void udp_tracker_connection::scrape_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
fail(-1, "incorrect transaction id");
return;
}
if (action == action_error)
{
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != action_scrape)
{
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int complete = detail::read_int32(buf);
/*int downloaded = */detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
requester().tracker_response(tracker_req(), peer_list, 0
, complete, incomplete);
m_man.remove_request(this);
}
catch (std::exception& e)
{
fail(-1, e.what());
}
}

455
cpp/web_peer_connection.cpp Executable file
View File

@@ -0,0 +1,455 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <iostream>
#include <iomanip>
#include <limits>
#include <boost/bind.hpp>
#include <sstream>
#include "libtorrent/web_peer_connection.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/identify_client.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/version.hpp"
#include "libtorrent/aux_/session_impl.hpp"
using namespace boost::posix_time;
using boost::bind;
using boost::shared_ptr;
using libtorrent::aux::session_impl;
namespace libtorrent
{
web_peer_connection::web_peer_connection(
session_impl& ses
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote
, std::string const& url)
: peer_connection(ses, t, s, remote)
, m_url(url)
, m_first_request(true)
{
INVARIANT_CHECK;
m_max_out_request_queue = ses.settings().urlseed_pipeline_size;
// since this is a web seed, change the timeout
// according to the settings.
set_timeout(ses.settings().urlseed_timeout);
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << "*** web_peer_connection\n";
#endif
std::string protocol;
boost::tie(protocol, m_host, m_port, m_path)
= parse_url_components(url);
m_server_string = "URL seed @ ";
m_server_string += m_host;
}
web_peer_connection::~web_peer_connection()
{}
boost::optional<piece_block_progress>
web_peer_connection::downloading_piece_progress() const
{
if (!m_parser.header_finished() || m_requests.empty())
return boost::optional<piece_block_progress>();
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
int body_start = m_parser.body_start();
buffer::const_interval recv_buffer = receive_buffer();
assert(body_start <= recv_buffer.left());
piece_block_progress ret;
ret.piece_index = m_requests.front().piece;
ret.block_index = m_requests.front().start / t->block_size();
ret.bytes_downloaded = recv_buffer.left() - body_start;
ret.full_block_bytes = m_requests.front().length;
return ret;
}
void web_peer_connection::on_connected()
{
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
// this is always a seed
incoming_bitfield(std::vector<bool>(
t->torrent_file().num_pieces(), true));
// it is always possible to request pieces
incoming_unchoke();
reset_recv_buffer(512*1024+1024);
}
void web_peer_connection::write_request(peer_request const& r)
{
INVARIANT_CHECK;
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
assert(t->valid_metadata());
bool single_file_request = false;
if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
single_file_request = true;
torrent_info const& info = t->torrent_file();
std::string request;
m_requests.push_back(r);
bool using_proxy = false;
if (!m_ses.settings().proxy_ip.empty())
using_proxy = true;
if (single_file_request)
{
request += "GET ";
if (using_proxy) request += m_url;
else request += escape_path(m_path.c_str(), m_path.length());
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += m_ses.settings().user_agent;
}
if (using_proxy && !m_ses.settings().proxy_login.empty())
{
request += "\r\nProxy-Authorization: Basic ";
request += base64encode(m_ses.settings().proxy_login + ":"
+ m_ses.settings().proxy_password);
}
if (using_proxy)
{
request += "\r\nProxy-Connection: keep-alive";
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start);
request += "-";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start + r.length - 1);
if (m_first_request || using_proxy)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(0);
}
else
{
std::vector<file_slice> files = info.map_block(r.piece, r.start
, r.length);
for (std::vector<file_slice>::iterator i = files.begin();
i != files.end(); ++i)
{
file_slice const& f = *i;
request += "GET ";
if (using_proxy)
{
request += m_url;
std::string path = info.file_at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
else
{
std::string path = m_path;
path += info.file_at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += m_ses.settings().user_agent;
}
if (using_proxy && !m_ses.settings().proxy_login.empty())
{
request += "\r\nProxy-Authorization: Basic ";
request += base64encode(m_ses.settings().proxy_login + ":"
+ m_ses.settings().proxy_password);
}
if (using_proxy)
{
request += "\r\nProxy-Connection: keep-alive";
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(f.offset);
request += "-";
request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
if (m_first_request || using_proxy)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(f.file_index);
}
}
send_buffer(request.c_str(), request.c_str() + request.size());
}
// --------------------------
// RECEIVE DATA
// --------------------------
// throws exception when the client should be disconnected
void web_peer_connection::on_receive(const asio::error& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error)
{
return;
}
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
incoming_piece_fragment();
for (;;)
{
buffer::const_interval recv_buffer = receive_buffer();
int payload;
int protocol;
boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
m_statistics.received_bytes(payload, protocol);
if (m_parser.status_code() != 206 && m_parser.status_code() != -1)
{
// we should not try this server again.
t->remove_url_seed(m_url);
if (m_parser.status_code() == 404)
throw std::runtime_error("File not found on server");
throw std::runtime_error("HTTP server does not support byte range requests");
}
if (!m_parser.finished()) break;
std::string server_version = m_parser.header<std::string>("Server");
if (!server_version.empty())
{
m_server_string = "URL seed @ ";
m_server_string += m_host;
m_server_string += " (";
m_server_string += server_version;
m_server_string += ")";
}
std::stringstream range_str(m_parser.header<std::string>("Content-Range"));
size_type range_start;
size_type range_end;
char dummy;
std::string bytes;
range_str >> bytes >> range_start >> dummy >> range_end;
if (!range_str)
{
// we should not try this server again.
t->remove_url_seed(m_url);
throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
}
// the http range is inclusive
range_end++;
torrent_info const& info = t->torrent_file();
if (m_requests.empty() || m_file_requests.empty())
throw std::runtime_error("unexpected HTTP response");
int file_index = m_file_requests.front();
m_file_requests.pop_front();
peer_request r = info.map_file(file_index, range_start
, range_end - range_start);
buffer::const_interval http_body = m_parser.get_body();
if (r == m_requests.front())
{
m_requests.pop_front();
incoming_piece(r, http_body.begin);
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
return;
}
if (!m_piece.empty())
{
// this is not the first partial request we get
if (m_intermediate_piece.start + m_intermediate_piece.length != r.start
|| m_intermediate_piece.piece != r.piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
}
else
{
// this is the first part of a partial request
if (r.start != m_requests.front().start
|| r.piece != m_requests.front().piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
m_intermediate_piece.piece = r.piece;
m_intermediate_piece.start = r.start;
m_intermediate_piece.length = 0;
}
m_piece.reserve(info.piece_length());
std::copy(http_body.begin, http_body.end, back_inserter(m_piece));
m_intermediate_piece.length += r.length;
if (m_intermediate_piece.length == m_requests.front().length)
{
assert(m_requests.front() == m_intermediate_piece);
assert(int(m_piece.size()) == m_intermediate_piece.length);
m_requests.pop_front();
incoming_piece(m_intermediate_piece, &m_piece[0]);
m_piece.clear();
}
else if (m_intermediate_piece.length > m_requests.front().length)
{
throw std::runtime_error("too large HTTP response body");
}
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
}
}
// --------------------------
// SEND DATA
// --------------------------
void web_peer_connection::get_peer_info(peer_info& p) const
{
assert(!associated_torrent().expired());
p.down_speed = statistics().download_rate();
p.up_speed = statistics().upload_rate();
p.payload_down_speed = statistics().download_payload_rate();
p.payload_up_speed = statistics().upload_payload_rate();
p.pid = pid();
p.ip = remote();
p.total_download = statistics().total_payload_download();
p.total_upload = statistics().total_payload_upload();
if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
p.upload_limit = -1;
else
p.upload_limit = m_ul_bandwidth_quota.given;
if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
p.download_limit = -1;
else
p.download_limit = m_dl_bandwidth_quota.given;
p.load_balancing = total_free_upload();
p.download_queue_length = (int)download_queue().size();
p.upload_queue_length = (int)upload_queue().size();
if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
{
p.downloading_piece_index = ret->piece_index;
p.downloading_block_index = ret->block_index;
p.downloading_progress = ret->bytes_downloaded;
p.downloading_total = ret->full_block_bytes;
}
else
{
p.downloading_piece_index = -1;
p.downloading_block_index = -1;
p.downloading_progress = 0;
p.downloading_total = 0;
}
p.flags = 0;
if (is_interesting()) p.flags |= peer_info::interesting;
if (is_choked()) p.flags |= peer_info::choked;
if (is_peer_interested()) p.flags |= peer_info::remote_interested;
if (has_peer_choked()) p.flags |= peer_info::remote_choked;
if (is_local()) p.flags |= peer_info::local_connection;
if (!is_connecting() && m_server_string.empty())
p.flags |= peer_info::handshake;
if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
if (is_queued()) p.flags |= peer_info::queued;
p.pieces = get_bitfield();
p.seed = is_seed();
p.client = m_server_string;
p.connection_type = peer_info::web_seed;
}
// throws exception when the client should be disconnected
void web_peer_connection::on_sent(asio::error const& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error) return;
m_statistics.sent_bytes(0, bytes_transferred);
}
#ifndef NDEBUG
void web_peer_connection::check_invariant() const
{
/*
assert(m_num_pieces == std::count(
m_have_piece.begin()
, m_have_piece.end()
, true));
*/ }
#endif
}

622
flood.py Normal file
View File

@@ -0,0 +1,622 @@
#
# Copyright (C) 2006 Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Deluge Library, a.k.a. Flood, previously known as python-libtorrent:
#
# Flood is a Python library for torrenting, that includes
# Flood, which is Python code, and Flood_core, which is also a Python
# module, but written in C++, and includes the libtorrent torrent library. Only
# Flood should be visible, and only it should be imported, in the client.
# Flood_core contains mainly libtorrent-interfacing code, and a few other things
# that make most sense to write at that level. Flood contains all other
# torrent-system management: queueing, configuration management, persistent
# list of torrents, etc.
#
# Documentation:
# Torrents have 3 structures:
# 1. torrent_info - persistent data, like name, upload speed cap, etc.
# 2. core_torrent_state - transient state data from the core. This may take
# time to calculate, so we do if efficiently
# 3. supp_torrent_state - supplementary torrent data, from Flood
import flood_core
import os, shutil
import pickle
import time
# Constants
TORRENTS_SUBDIR = "torrentfiles"
STATE_FILENAME = "persistent.state"
PREFS_FILENAME = "prefs.state"
DHT_FILENAME = "dht.state"
CACHED_DATA_EXPIRATION = 1 # seconds, like the output of time.time()
# "max_half_open" : -1,
DEFAULT_PREFS = {
"max_uploads" : 2, # a.k.a. upload slots
"listen_on" : [6881,9999],
"max_connections" : 80,
"use_DHT" : True,
"max_active_torrents" : -1,
"auto_seed_ratio" : -1,
"max_download_rate" : -1,
"max_upload_rate" : -1
}
PREF_FUNCTIONS = {
"max_uploads" : flood_core.set_max_uploads,
"listen_on" : flood_core.set_listen_on,
"max_connections" : flood_core.set_max_connections,
"use_DHT" : None, # not a normal pref in that is is applied only on start
"max_active_torrents" : None, # no need for a function, applied constantly
"auto_seed_ratio" : None, # no need for a function, applied constantly
"max_download_rate" : flood_core.set_download_rate_limit,
"max_upload_rate" : flood_core.set_upload_rate_limit
}
# Exceptions
class FloodError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class InvalidEncodingError(FloodError):
pass
class FilesystemError(FloodError):
pass
class DuplicateTorrentError(FloodError):
pass
class InvalidTorrentError(FloodError):
pass
# A cached data item
class cached_data:
def __init__(self, get_method, key):
self.get_method = get_method
self.key = key
self.timestamp = -1
def get(self, efficiently=True):
if self.timestamp == -1 or time.time() > self.timestamp + CACHED_DATA_EXPIRATION or \
not efficiently:
self.data = self.get_method(self.key)
self.timestamp = time.time()
return self.data
# Persistent information for a single torrent
class torrent_info:
def __init__(self, filename, save_dir, compact):
self.filename = filename
self.save_dir = save_dir
self.compact = compact
self.user_paused = False # start out unpaused
self.uploaded_memory = 0
self.delete_me = False # set this to true, to delete it on next sync
# The persistent state of the torrent system. Everything in this will be pickled
class persistent_state:
def __init__(self):
# Torrents
self.torrents = []
# Prepare queue (queue is pickled, just like everything else)
self.queue = [] # queue[x] is the unique_ID of the x-th queue position. Simple.
# The manager for the torrent system
class manager:
# blank_slate mode ignores the two pickle files and DHT state file, i.e. you start
# completely fresh. When quitting, the old files will be overwritten
def __init__(self, client_ID, version, user_agent, base_dir, blank_slate=False):
self.base_dir = base_dir
# Ensure directories exist
if not TORRENTS_SUBDIR in os.listdir(self.base_dir):
os.mkdir(self.base_dir + "/" + TORRENTS_SUBDIR)
# Pre-initialize the core's data structures
flood_core.pre_init(FloodError,
InvalidEncodingError,
FilesystemError,
DuplicateTorrentError,
InvalidTorrentError)
# Start up the core
assert(len(version) == 4)
flood_core.init(client_ID,
int(version[0]),
int(version[1]),
int(version[2]),
int(version[3]),
user_agent)
self.constants = flood_core.constants()
# Unique IDs are NOT in the state, since they are temporary for each session
self.unique_IDs = {} # unique_ID -> a torrent_info object, i.e. persistent data
# Saved torrent core_states. We do not poll the core in a costly manner, necessarily
self.saved_core_torrent_states = {} # unique_ID -> torrent_state
# supplementary torrent states
self.supp_torrent_states = {} # unique_ID->dict of data
# Saved torrent core_states. We do not poll the core in a costly manner, necessarily
self.saved_core_torrent_peer_infos = {} # unique_ID -> torrent_state
# Unpickle the preferences, or create a new one
self.prefs = DEFAULT_PREFS
if not blank_slate:
try:
pkl_file = open(self.base_dir + "/" + PREFS_FILENAME, 'rb')
self.prefs = pickle.load(pkl_file)
pkl_file.close()
except IOError:
pass
# Apply preferences. Note that this is before any torrents are added
self.apply_prefs()
# Apply DHT, if needed. Note that this is before any torrents are added
if self.get_pref('use_DHT'):
if not blank_slate:
flood_core.start_DHT(self.base_dir + "/" + DHT_FILENAME)
else:
flood_core.start_DHT("")
# Unpickle the state, or create a new one
if not blank_slate:
try:
pkl_file = open(self.base_dir + "/" + STATE_FILENAME, 'rb')
self.state = pickle.load(pkl_file)
pkl_file.close()
# Sync with the core: tell core about torrents, and get unique_IDs
self.sync()
# Apply all the file filters, right after adding the torrents
self.apply_all_file_filters()
except IOError:
self.state = persistent_state()
else:
self.state = persistent_state()
def quit(self):
# Analyze data needed for pickling, etc.
self.pre_quitting()
# Pickle the prefs
print "Pickling prefs..."
output = open(self.base_dir + "/" + PREFS_FILENAME, 'wb')
pickle.dump(self.prefs, output)
output.close()
# Pickle the state
print "Pickling state..."
output = open(self.base_dir + "/" + STATE_FILENAME, 'wb')
pickle.dump(self.state, output)
output.close()
# Save fastresume data
print "Saving fastresume data..."
self.save_fastresume_data()
# Stop DHT, if needed
if self.get_pref('use_DHT'):
print "Stopping DHT..."
flood_core.stop_DHT(self.base_dir + "/" + DHT_FILENAME)
# Shutdown torrent core
print "Quitting the core..."
flood_core.quit()
def pre_quitting(self):
# Save the uploaded data from this session to the existing upload memory
for unique_ID in self.unique_IDs.keys():
self.unique_IDs[unique_ID].uploaded_memory = \
self.unique_IDs[unique_ID].uploaded_memory + \
self.get_core_torrent_state(unique_ID, False)['total_upload'] # Purposefully ineffi.
# Preference management functions
def get_pref(self, key):
# If we have a value, return, else fallback on default_prefs, else raise an error
# the fallback is useful if the source has newer prefs than the existing pref state,
# which was created by an old version of the source
if key in self.prefs.keys():
return self.prefs[key]
elif key in DEFAULT_PREFS:
self.prefs[key] = DEFAULT_PREFS[key]
return self.prefs[key]
else:
raise FloodError("Asked for a pref that doesn't exist: " + key)
def set_pref(self, key, value):
# Make sure this is a valid key
if key not in DEFAULT_PREFS.keys():
raise FloodError("Asked to change a pref that isn't valid: " + key)
self.prefs[key] = value
# Apply the pref, if applicable
if PREF_FUNCTIONS[key] is not None:
PREF_FUNCTIONS[key](value)
# Torrent addition and removal functions
def add_torrent(self, filename, save_dir, compact):
self.add_torrent_ns(filename, save_dir, compact)
return self.sync() # Syncing will create a new torrent in the core, and return it's ID
def remove_torrent(self, unique_ID, data_also):
# Save some data before we remove the torrent, needed later in this func
temp = self.unique_IDs[unique_ID]
temp_fileinfo = flood_core.get_fileinfo(unique_ID)
self.remove_torrent_ns(unique_ID)
self.sync()
# Remove .torrent and .fastresume
os.remove(temp.filename)
try:
# Must be after removal of the torrent, because that saves a new .fastresume
os.remove(temp.filename + ".fastresume")
except OSError:
pass # Perhaps there never was one to begin with
# Remove data, if asked to do so
if data_also:
# Must be done AFTER the torrent is removed
# Note: can this be to the trash?
for filedata in temp_fileinfo:
filename = filedata['path']
try:
os.remove(temp.save_dir + "/" + filename)
except OSError:
pass # No file just means it wasn't downloaded, we can continue
# A separate function, because people may want to call it from time to time
def save_fastresume_data(self):
for unique_ID in self.unique_IDs:
flood_core.save_fastresume(unique_ID, self.unique_IDs[unique_ID].filename)
# State retrieval functions
def get_state(self):
ret = flood_core.get_session_info()
# Get additional data from our level
ret['is_listening'] = flood_core.is_listening()
ret['port'] = flood_core.listening_port()
if self.get_pref('use_DHT'):
ret['DHT_nodes'] = flood_core.get_DHT_info()
return ret
# This is the EXTERNAL function, for the GUI. It returns the core_state + supp_state
def get_torrent_state(self, unique_ID):
ret = self.get_core_torrent_state(unique_ID, True).copy()
# Add the flood-level things to the flood_core data
if self.get_supp_torrent_state(unique_ID) is not None:
ret.update(self.get_supp_torrent_state(unique_ID))
# Get queue position
ret['queue_pos'] = self.queue.index(unique_ID)
return ret
def get_torrent_peer_info(self, unique_ID):
# Perhaps at some time we may add info here
return get_core_torrent_peer_info(unique_ID)
# Queueing functions
def queue_up(self, unique_ID):
curr_index = self.get_queue_index(unique_ID)
if curr_index > 0:
temp = self.state.queue[curr_index - 1]
self.state.queue[curr_index - 1] = unique_ID
self.state.queue[curr_index] = temp
def queue_down(self, unique_ID):
curr_index = self.get_queue_index(unique_ID)
if curr_index < (len(self.state.queue) - 1):
temp = self.state.queue[curr_index + 1]
self.state.queue[curr_index + 1] = unique_ID
self.state.queue[curr_index] = temp
def queue_bottom(self, unique_ID):
curr_index = self.get_queue_index(unique_ID)
if curr_index < (len(self.state.queue) - 1):
self.state.queue.remove(curr_index)
self.state.queue.append(unique_ID)
def clear_completed(self):
for unique_ID in self.unique_IDs:
torrent_state = self.get_core_torrent_state(unique_ID)
if torrent_state['progress'] == 100.0:
self.remove_torrent_ns(unique_ID)
self.sync()
# Enforce the queue: pause/unpause as needed, based on queue and user_pausing
# This should be called after changes to relevant parameters (user_pausing, or
# altering max_active_torrents), or just from time to time
# ___ALL queuing code should be in this function, and ONLY here___
def apply_queue(self, efficient = True):
# Handle autoseeding - downqueue as needed
if self.auto_seed_ratio != -1:
for unique_ID in self.unique_IDs:
if self.get_core_torrent_state(unique_ID, efficient)['is_seed']:
torrent_state = self.get_core_torrent_state(unique_ID, efficient)
ratio = self.calc_ratio(unique_ID, torrent_state)
if ratio >= self.auto_seed_ratio:
self.queue_bottom(unique_ID)
# Pause and resume torrents
for index in range(len(self.state.queue)):
unique_ID = self.state.queue[index]
if (index < self.state.max_active_torrents or self.state_max_active_torrents == -1) \
and self.get_core_torrent_state(unique_ID, efficient)['is_paused'] \
and not self.is_user_paused(unique_ID):
flood_core.resume(unique_ID)
elif not self.get_core_torrent_state(unique_ID, efficient)['is_paused'] and \
(index >= self.state.max_active_torrents or self.is_user_paused(unique_ID)):
flood_core.pause(unique_ID)
# Event handling
def handle_events(self):
# Handle them for the backend's purposes, but still send them up in case the client
# wants to do something - show messages, for example
ret = []
event = flood_core.pop_event()
while event is not None:
# print "EVENT: ", event
ret.append(event)
if event['event_type'] is self.constants['EVENT_FINISHED']:
# If we are autoseeding, then we need to apply the queue
if self.auto_seed_ratio == -1:
self.apply_queue(efficient = False) # To work on current data
elif event['event_type'] is self.constants['EVENT_TRACKER']:
unique_ID = event['unique_ID']
status = event['tracker_status']
message = event['message']
tracker = message[message.find('"')+1:message.rfind('"')]
self.set_supp_torrent_state_val(unique_ID,
"tracker_status",
(tracker, status))
old_state = self.get_supp_torrent_state(unique_ID)
try:
new = old_state['tracker_messages']
except KeyError:
new = {}
new[tracker] = message
self.set_supp_torrent_state_val(unique_ID,
"tracker_messages",
new)
event = flood_core.pop_event()
return ret
# Filtering functions
def set_file_filter(self, unique_ID, file_filter):
assert(len(file_filter) == self.get_core_torrent_state(unique_ID, True)['num_files'])
self.unique_IDs[unique_ID].file_filter = file_filter[:]
flood_core.set_filter_out(file_filter)
def get_file_filter(self, unique_ID):
try:
return self.unique_IDs[unique_ID].file_filter[:]
except AttributeError:
return None
# Called when a session starts, to apply existing filters
def apply_all_file_filters(self):
for unique_ID in self.unique_IDs.keys():
try:
self.set_file_filter(unique_ID, self.unique_IDs[unique_ID].file_filter)
except AttributeError:
pass
# Advanced statistics - these may be SLOW. The client should call these only
# when needed, and perhaps only once in a long while (they are mostly just
# approximations anyhow
def calc_availability(self, unique_ID):
return flood_stats.calc_availability(self.get_core_torrent_peer_info(unique_ID))
def calc_swarm_speed(self, unique_ID):
pieces_per_sec = flood_stats.calc_swarm_speed(self.get_core_torrent_peer_info(unique_ID))
piece_length = self.get_core_torrent_state(unique_ID, efficiently=True)
return pieces_per_sec * piece_length
# Miscellaneous minor functions
def set_user_pause(self, unique_ID, new_value):
self.unique_IDs[unique_ID].user_paused = new_value
self.apply_queue()
def is_user_paused(self, unique_ID):
return self.unique_IDs[unique_ID].user_paused
def get_num_torrents(self):
return flood_core.get_num_torrents()
def get_unique_IDs(self):
return self.unique_IDs.keys()
####################
# Internal functions
####################
# Efficient: use a saved state, if it hasn't expired yet
def get_core_torrent_state(self, unique_ID, efficiently=True):
if unique_ID not in self.saved_core_torrent_states.keys():
self.saved_core_torrent_states[unique_ID] = cached_data(flood_core.get_torrent_state,
unique_ID)
return self.saved_core_torrent_states[unique_ID].get(efficiently)
def get_supp_torrent_state(self, unique_ID):
try:
return self.supp_torrent_states[unique_ID]
except KeyError:
return None
def set_supp_torrent_state_val(self, unique_ID, key, val):
try:
if self.supp_torrent_states[unique_ID] is None:
self.supp_torrent_states[unique_ID] = {}
except KeyError:
self.supp_torrent_states[unique_ID] = {}
self.supp_torrent_states[unique_ID][key] = val
def get_core_torrent_peer_info(self, unique_ID, efficiently=True):
if unique_ID not in self.saved_torrent_peer_infos.keys():
self.saved_torrent_peer_infos[unique_ID] = cached_data(flood_core.get_peer_info,
unique_ID)
return self.saved_torrent_peer_infos[unique_ID].get(efficiently)
# Non-syncing functions. Used when we loop over such events, and sync manually at the end
def add_torrent_ns(self, filename, save_dir, compact):
# Cache torrent file
(temp, filename_short) = os.path.split(filename)
if filename_short in os.listdir(self.base_dir + "/" + TORRENTS_SUBDIR):
raise FloodError("Duplicate Torrent, it appears: " + filename_short)
full_new_name = self.base_dir + "/" + TORRENTS_SUBDIR + "/" + filename_short
shutil.copy(filename, full_new_name)
# Create torrent object
new_torrent = torrent_info(full_new_name, save_dir, compact)
self.state.torrents.append(new_torrent)
def remove_torrent_ns(self, unique_ID):
self.unique_IDs[unique_ID].delete_me = True
# Sync the state.torrents and unique_IDs lists with the core
# ___ALL syncing code with the core is here, and ONLY here___
# Also all self-syncing is done here (various lists)
def sync(self):
ret = None # We return new added unique ID(s), or None
# Add torrents to core and unique_IDs
torrents_with_unique_ID = self.unique_IDs.values()
for torrent in self.state.torrents:
if torrent not in torrents_with_unique_ID:
# print "Adding torrent to core:", torrent.filename, torrent.save_dir, torrent.compact
unique_ID = flood_core.add_torrent(torrent.filename,
torrent.save_dir,
torrent.compact)
# print "Got unique ID:", unique_ID
ret = unique_ID
self.unique_IDs[unique_ID] = torrent
# Remove torrents from core, unique_IDs and queue
to_delete = []
for torrent in self.state.torrents:
if torrent.delete_me:
flood_core.remove_torrent(torrent.unique_ID, torrent.filename)
to_delete.append(torrent.unique_ID)
for unique_ID in to_delete:
self.state.torrents.remove(self.unique_IDs[unique_ID])
self.state.queue.remove(self.unique_IDs[unique_ID])
del self.unique_IDs[unique_ID]
# Add torrents to queue - at the end, of course
for unique_ID in self.unique_IDs:
if unique_ID not in self.state.queue:
self.state.queue.append(unique_ID)
assert(len(self.unique_IDs) == len(self.state.torrents))
assert(len(self.unique_IDs) == len(self.state.queue))
assert(len(self.unique_IDs) == flood_core.get_num_torrents())
return ret
def get_queue_index(self, unique_ID):
return self.state.queue.index(unique_ID)
def apply_prefs(self):
print "Applying preferences"
assert(len(PREF_FUNCTIONS) == len(DEFAULT_PREFS))
for pref in PREF_FUNCTIONS.keys():
if PREF_FUNCTIONS[pref] is not None:
PREF_FUNCTIONS[pref](self.get_pref(pref))
# Calculations
def calc_ratio(self, unique_ID, torrent_state):
up = float(torrent_state['total_upload'] + self.unique_IDs[unique_ID].uploaded_memory)
down = float(torrent_state["total_done"])
try:
ret = float(up/down)
except:
ret = -1
return ret

78
flood_stats.py Normal file
View File

@@ -0,0 +1,78 @@
#
# Copyright (C) 2006 Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
import time
# Global variables. Caching of saved data, mostly
old_peer_info = None
old_peer_info_timestamp = None
# Availability - how many complete copies are among our peers
def calc_availability(peer_info):
if len(peer_info) == 0:
return 0
num_pieces = len(peer_info[0].pieces)
freqs = [0]*num_pieces
for peer in peer_info:
for piece in num_pieces:
freqs[piece] = freqs[piece] + peer['pieces'][piece]
minimum = min(freqs)
# frac = freqs.count(minimum + 1) # Does this mean something?
return minimum
# Swarm speed - try to guess the speed of the entire swarm
# We return #pieces / second. The calling function should convert pieces to KB, if it wants
# Note that we return the delta from the last call. If the client calls too soon, this may
# be too unreliable. But the client can smooth things out, if desired
def calc_swarm_speed(peer_info):
if old_peer_info is not None:
new_pieces = 0
peers_known = 0
# List new peers
new_peer_IPs = {} # ip->peerinfo dict (from the core)
for peer in peer_info:
new_peer_IPs[peer['ip']] = peer
for new_IP in new_peer_IPs.keys():
if new_IP in old_peer_IPs.keys():
# We know this peer from before, see what changed
peers_known = peers_known + 1
delta = sum(new_peer_IPs[new_IP].pieces) - sum(old_peer_IPs[new_IP].pieces)
if delta >= 0:
new_pieces = new_pieces + delta
else:
print "Flood.stat.calc_swarm_speed: Bad Delta: ", delta, old_peer_IPs[new_IP].pieces, new_peer_IPs[new_IP].pieces
# Calculate final value
time_delta = time.time() - old_peer_info_timestamp
ret = float(new_pieces)/( float(peers_known) * time_delta )
# Save info
old_peer_info = peer_info
old_peer_info_timestamp = time.time()
old_peer_IPs = new_peer_IPs
return ret

174
include/libtorrent/alert.hpp Executable file
View File

@@ -0,0 +1,174 @@
/*
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALERT_HPP_INCLUDED
#define TORRENT_ALERT_HPP_INCLUDED
#include <memory>
#include <queue>
#include <string>
#include <cassert>
#include <typeinfo>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/thread/mutex.hpp>
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/config.hpp"
#define TORRENT_MAX_ALERT_TYPES 10
namespace libtorrent {
class TORRENT_EXPORT alert
{
public:
enum severity_t { debug, info, warning, critical, fatal, none };
alert(severity_t severity, const std::string& msg);
virtual ~alert();
// a timestamp is automatically created in the constructor
boost::posix_time::ptime timestamp() const;
const std::string& msg() const;
severity_t severity() const;
virtual std::auto_ptr<alert> clone() const = 0;
private:
std::string m_msg;
severity_t m_severity;
boost::posix_time::ptime m_timestamp;
};
class TORRENT_EXPORT alert_manager
{
public:
alert_manager();
~alert_manager();
void post_alert(const alert& alert_);
bool pending() const;
std::auto_ptr<alert> get();
void set_severity(alert::severity_t severity);
bool should_post(alert::severity_t severity) const;
private:
std::queue<alert*> m_alerts;
alert::severity_t m_severity;
mutable boost::mutex m_mutex;
};
struct TORRENT_EXPORT unhandled_alert : std::exception
{
unhandled_alert() {}
};
namespace detail {
struct void_;
template<
class Handler
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T)
>
void handle_alert_dispatch(
const std::auto_ptr<alert>& alert_
, const Handler& handler
, const std::type_info& typeid_
, BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p))
{
if (typeid_ == typeid(T0))
handler(*static_cast<T0*>(alert_.get()));
else
handle_alert_dispatch(
alert_
, handler
, typeid_
, BOOST_PP_ENUM_SHIFTED_PARAMS(TORRENT_MAX_ALERT_TYPES, p), (void_*)0
);
}
template<class Handler>
void handle_alert_dispatch(
const std::auto_ptr<alert>& alert_
, const Handler& handler
, const std::type_info& typeid_
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT))
{
throw unhandled_alert();
}
} // namespace detail
template<
BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_)
>
struct TORRENT_EXPORT handle_alert
{
template<class Handler>
handle_alert(
const std::auto_ptr<alert>& alert_
, const Handler& handler)
{
#define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0
detail::handle_alert_dispatch(
alert_
, handler
, typeid(*alert_)
, BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)
);
#undef ALERT_POINTER_TYPE
}
};
} // namespace libtorrent
#endif // TORRENT_ALERT_HPP_INCLUDED

View File

@@ -0,0 +1,300 @@
/*
Copyright (c) 2003, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED
#define TORRENT_ALERT_TYPES_HPP_INCLUDED
#include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/config.hpp"
namespace libtorrent
{
struct TORRENT_EXPORT tracker_alert: alert
{
tracker_alert(torrent_handle const& h
, int times
, int status
, std::string const& msg)
: alert(alert::warning, msg)
, handle(h)
, times_in_row(times)
, status_code(status)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new tracker_alert(*this)); }
torrent_handle handle;
int times_in_row;
int status_code;
};
struct TORRENT_EXPORT tracker_warning_alert: alert
{
tracker_warning_alert(torrent_handle const& h
, std::string const& msg)
: alert(alert::warning, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new tracker_warning_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT tracker_reply_alert: alert
{
tracker_reply_alert(torrent_handle const& h
, std::string const& msg)
: alert(alert::info, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new tracker_reply_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT tracker_announce_alert: alert
{
tracker_announce_alert(torrent_handle const& h, std::string const& msg)
: alert(alert::info, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new tracker_announce_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT hash_failed_alert: alert
{
hash_failed_alert(
torrent_handle const& h
, int index
, std::string const& msg)
: alert(alert::info, msg)
, handle(h)
, piece_index(index)
{ assert(index >= 0);}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new hash_failed_alert(*this)); }
torrent_handle handle;
int piece_index;
};
struct TORRENT_EXPORT peer_ban_alert: alert
{
peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg)
: alert(alert::info, msg)
, ip(pip)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new peer_ban_alert(*this)); }
tcp::endpoint ip;
torrent_handle handle;
};
struct TORRENT_EXPORT peer_error_alert: alert
{
peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg)
: alert(alert::debug, msg)
, ip(pip)
, pid(pid_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new peer_error_alert(*this)); }
tcp::endpoint ip;
peer_id pid;
};
struct TORRENT_EXPORT chat_message_alert: alert
{
chat_message_alert(
const torrent_handle& h
, const tcp::endpoint& sender
, const std::string& msg)
: alert(alert::critical, msg)
, handle(h)
, ip(sender)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new chat_message_alert(*this)); }
torrent_handle handle;
tcp::endpoint ip;
};
struct TORRENT_EXPORT invalid_request_alert: alert
{
invalid_request_alert(
peer_request const& r
, torrent_handle const& h
, tcp::endpoint const& sender
, peer_id const& pid_
, std::string const& msg)
: alert(alert::debug, msg)
, handle(h)
, ip(sender)
, request(r)
, pid(pid_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new invalid_request_alert(*this)); }
torrent_handle handle;
tcp::endpoint ip;
peer_request request;
peer_id pid;
};
struct TORRENT_EXPORT torrent_finished_alert: alert
{
torrent_finished_alert(
const torrent_handle& h
, const std::string& msg)
: alert(alert::warning, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT url_seed_alert: alert
{
url_seed_alert(
const std::string& url_
, const std::string& msg)
: alert(alert::warning, msg)
, url(url_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new url_seed_alert(*this)); }
std::string url;
};
struct TORRENT_EXPORT file_error_alert: alert
{
file_error_alert(
const torrent_handle& h
, const std::string& msg)
: alert(alert::fatal, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new file_error_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT metadata_failed_alert: alert
{
metadata_failed_alert(
const torrent_handle& h
, const std::string& msg)
: alert(alert::info, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new metadata_failed_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT metadata_received_alert: alert
{
metadata_received_alert(
const torrent_handle& h
, const std::string& msg)
: alert(alert::info, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new metadata_received_alert(*this)); }
torrent_handle handle;
};
struct TORRENT_EXPORT listen_failed_alert: alert
{
listen_failed_alert(
const std::string& msg)
: alert(alert::fatal, msg)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
};
struct TORRENT_EXPORT fastresume_rejected_alert: alert
{
fastresume_rejected_alert(torrent_handle const& h
, std::string const& msg)
: alert(alert::warning, msg)
, handle(h)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new fastresume_rejected_alert(*this)); }
torrent_handle handle;
};
}
#endif

View File

@@ -0,0 +1,72 @@
/*
Copyright (c) 2003, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#include <map>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "libtorrent/resource_request.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
namespace libtorrent
{
class peer_connection;
class torrent;
int saturated_add(int a, int b);
// Function to allocate a limited resource fairly among many consumers.
// It takes into account the current use, and the consumer's desired use.
// Should be invoked periodically to allow it adjust to the situation (make
// sure "used" is updated between calls!).
// If resources = std::numeric_limits<int>::max() it means there is an infinite
// supply of resources (so everyone can get what they want).
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& torrents
, resource_request torrent::* res);
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& connections
, resource_request peer_connection::* res);
}
#endif

View File

@@ -0,0 +1,71 @@
//
// asio.hpp
// ~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_HPP
#define ASIO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/basic_datagram_socket.hpp"
#include "asio/basic_deadline_timer.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/basic_resolver.hpp"
#include "asio/basic_socket_acceptor.hpp"
#include "asio/basic_socket_iostream.hpp"
#include "asio/basic_socket_streambuf.hpp"
#include "asio/basic_stream_socket.hpp"
#include "asio/basic_streambuf.hpp"
#include "asio/buffer.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/buffered_stream.hpp"
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/completion_condition.hpp"
#include "asio/datagram_socket_service.hpp"
#include "asio/deadline_timer_service.hpp"
#include "asio/deadline_timer.hpp"
#include "asio/error_handler.hpp"
#include "asio/error.hpp"
#include "asio/handler_alloc_hook.hpp"
#include "asio/handler_invoke_hook.hpp"
#include "asio/io_service.hpp"
#include "asio/ip/address.hpp"
#include "asio/ip/address_v4.hpp"
#include "asio/ip/address_v6.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver_entry.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
#include "asio/ip/host_name.hpp"
#include "asio/ip/multicast.hpp"
#include "asio/ip/resolver_query_base.hpp"
#include "asio/ip/tcp.hpp"
#include "asio/ip/udp.hpp"
#include "asio/is_read_buffered.hpp"
#include "asio/is_write_buffered.hpp"
#include "asio/placeholders.hpp"
#include "asio/read.hpp"
#include "asio/read_until.hpp"
#include "asio/resolver_service.hpp"
#include "asio/socket_acceptor_service.hpp"
#include "asio/socket_base.hpp"
#include "asio/strand.hpp"
#include "asio/stream_socket_service.hpp"
#include "asio/streambuf.hpp"
#include "asio/system_exception.hpp"
#include "asio/thread.hpp"
#include "asio/time_traits.hpp"
#include "asio/write.hpp"
#endif // ASIO_HPP

View File

@@ -0,0 +1,802 @@
//
// basic_datagram_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP
#define ASIO_BASIC_DATAGRAM_SOCKET_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_socket.hpp"
#include "asio/datagram_socket_service.hpp"
#include "asio/error_handler.hpp"
namespace asio {
/// Provides datagram-oriented socket functionality.
/**
* The basic_datagram_socket class template provides asynchronous and blocking
* datagram-oriented socket functionality.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Error_Source.
*/
template <typename Protocol,
typename Service = datagram_socket_service<Protocol> >
class basic_datagram_socket
: public basic_socket<Protocol, Service>
{
public:
/// The native representation of a socket.
typedef typename Service::native_type native_type;
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// Construct a basic_datagram_socket without opening it.
/**
* This constructor creates a datagram socket without opening it. The open()
* function must be called before data can be sent or received on the socket.
*
* @param io_service The io_service object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*/
explicit basic_datagram_socket(asio::io_service& io_service)
: basic_socket<Protocol, Service>(io_service)
{
}
/// Construct and open a basic_datagram_socket.
/**
* This constructor creates and opens a datagram socket.
*
* @param io_service The io_service object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::error Thrown on failure.
*/
basic_datagram_socket(asio::io_service& io_service,
const protocol_type& protocol)
: basic_socket<Protocol, Service>(io_service, protocol)
{
}
/// Construct a basic_datagram_socket, opening it and binding it to the given
/// local endpoint.
/**
* This constructor creates a datagram socket and automatically opens it bound
* to the specified endpoint on the local machine. The protocol used is the
* protocol associated with the given endpoint.
*
* @param io_service The io_service object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param endpoint An endpoint on the local machine to which the datagram
* socket will be bound.
*
* @throws asio::error Thrown on failure.
*/
basic_datagram_socket(asio::io_service& io_service,
const endpoint_type& endpoint)
: basic_socket<Protocol, Service>(io_service, endpoint)
{
}
/// Construct a basic_datagram_socket on an existing native socket.
/**
* This constructor creates a datagram socket object to hold an existing
* native socket.
*
* @param io_service The io_service object that the datagram socket will use
* to dispatch handlers for any asynchronous operations performed on the
* socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::error Thrown on failure.
*/
basic_datagram_socket(asio::io_service& io_service,
const protocol_type& protocol, const native_type& native_socket)
: basic_socket<Protocol, Service>(io_service, protocol, native_socket)
{
}
/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code socket.send(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers>
std::size_t send(const Const_Buffers& buffers)
{
return this->service.send(this->implementation, buffers, 0, throw_error());
}
/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One ore more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*/
template <typename Const_Buffers>
std::size_t send(const Const_Buffers& buffers,
socket_base::message_flags flags)
{
return this->service.send(this->implementation, buffers, flags,
throw_error());
}
/// Send some data on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes sent.
*
* @note The send operation can only be used with a connected socket. Use
* the send_to function to send data on an unconnected datagram socket.
*/
template <typename Const_Buffers, typename Error_Handler>
std::size_t send(const Const_Buffers& buffers,
socket_base::message_flags flags, Error_Handler error_handler)
{
return this->service.send(this->implementation, buffers, flags,
error_handler);
}
/// Start an asynchronous send on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected datagram
* socket.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers, typename Handler>
void async_send(const Const_Buffers& buffers, Handler handler)
{
this->service.async_send(this->implementation, buffers, 0, handler);
}
/// Start an asynchronous send on a connected socket.
/**
* This function is used to send data on the datagram socket. The function
* call will block until the data has been sent successfully or an error
* occurs.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The async_send operation can only be used with a connected socket.
* Use the async_send_to function to send data on an unconnected datagram
* socket.
*/
template <typename Const_Buffers, typename Handler>
void async_send(const Const_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
this->service.async_send(this->implementation, buffers, flags, handler);
}
/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.send_to(asio::buffer(data, size), destination);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers>
std::size_t send_to(const Const_Buffers& buffers,
const endpoint_type& destination)
{
return this->service.send_to(this->implementation, buffers, destination, 0,
throw_error());
}
/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*/
template <typename Const_Buffers>
std::size_t send_to(const Const_Buffers& buffers,
const endpoint_type& destination, socket_base::message_flags flags)
{
return this->service.send_to(this->implementation, buffers, destination,
flags, throw_error());
}
/// Send a datagram to the specified endpoint.
/**
* This function is used to send a datagram to the specified remote endpoint.
* The function call will block until the data has been sent successfully or
* an error occurs.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
*
* @param destination The remote endpoint to which the data will be sent.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes sent.
*/
template <typename Const_Buffers, typename Error_Handler>
std::size_t send_to(const Const_Buffers& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
Error_Handler error_handler)
{
return this->service.send_to(this->implementation, buffers, destination,
flags, error_handler);
}
/// Start an asynchronous send.
/**
* This function is used to asynchronously send a datagram to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* asio::ip::udp::endpoint destination(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.async_send_to(
* asio::buffer(data, size), destination, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers, typename Handler>
void async_send_to(const Const_Buffers& buffers,
const endpoint_type& destination, Handler handler)
{
this->service.async_send_to(this->implementation, buffers, destination, 0,
handler);
}
/// Start an asynchronous send.
/**
* This function is used to asynchronously send a datagram to the specified
* remote endpoint. The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent to the remote endpoint.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param destination The remote endpoint to which the data will be sent.
* Copies will be made of the endpoint as required.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*/
template <typename Const_Buffers, typename Handler>
void async_send_to(const Const_Buffers& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
Handler handler)
{
this->service.async_send_to(this->implementation, buffers, destination,
flags, handler);
}
/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.receive(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t receive(const Mutable_Buffers& buffers)
{
return this->service.receive(this->implementation, buffers, 0,
throw_error());
}
/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*/
template <typename Mutable_Buffers>
std::size_t receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags)
{
return this->service.receive(this->implementation, buffers, flags,
throw_error());
}
/// Receive some data on a connected socket.
/**
* This function is used to receive data on the datagram socket. The function
* call will block until data has been received successfully or an error
* occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes received.
*
* @note The receive operation can only be used with a connected socket. Use
* the receive_from function to receive data on an unconnected datagram
* socket.
*/
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags, Error_Handler error_handler)
{
return this->service.receive(this->implementation, buffers, flags,
error_handler);
}
/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the datagram
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* datagram socket.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive(const Mutable_Buffers& buffers, Handler handler)
{
this->service.async_receive(this->implementation, buffers, 0, handler);
}
/// Start an asynchronous receive on a connected socket.
/**
* This function is used to asynchronously receive data from the datagram
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The async_receive operation can only be used with a connected socket.
* Use the async_receive_from function to receive data on an unconnected
* datagram socket.
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
this->service.async_receive(this->implementation, buffers, flags, handler);
}
/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* asio::ip::udp::endpoint sender_endpoint;
* socket.receive_from(
* asio::buffer(data, size), sender_endpoint);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t receive_from(const Mutable_Buffers& buffers,
endpoint_type& sender_endpoint)
{
return this->service.receive_from(this->implementation, buffers,
sender_endpoint, 0, throw_error());
}
/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure.
*/
template <typename Mutable_Buffers>
std::size_t receive_from(const Mutable_Buffers& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags)
{
return this->service.receive_from(this->implementation, buffers,
sender_endpoint, flags, throw_error());
}
/// Receive a datagram with the endpoint of the sender.
/**
* This function is used to receive a datagram. The function call will block
* until data has been received successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes received.
*/
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t receive_from(const Mutable_Buffers& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
Error_Handler error_handler)
{
return this->service.receive_from(this->implementation, buffers,
sender_endpoint, flags, error_handler);
}
/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive a datagram. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code socket.async_receive_from(
* asio::buffer(data, size), 0, sender_endpoint, handler); @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive_from(const Mutable_Buffers& buffers,
endpoint_type& sender_endpoint, Handler handler)
{
this->service.async_receive_from(this->implementation, buffers,
sender_endpoint, 0, handler);
}
/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive a datagram. The function
* call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param sender_endpoint An endpoint object that receives the endpoint of
* the remote sender of the datagram. Ownership of the sender_endpoint object
* is retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive_from(const Mutable_Buffers& buffers,
endpoint_type& sender_endpoint, socket_base::message_flags flags,
Handler handler)
{
this->service.async_receive_from(this->implementation, buffers,
sender_endpoint, flags, handler);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP

View File

@@ -0,0 +1,309 @@
//
// basic_deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
#define ASIO_BASIC_DEADLINE_TIMER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/deadline_timer_service.hpp"
#include "asio/error.hpp"
namespace asio {
/// Provides waitable timer functionality.
/**
* The basic_deadline_timer class template provides the ability to perform a
* blocking or asynchronous wait for a timer to expire.
*
* Most applications will use the asio::deadline_timer typedef.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Error_Source.
*
* @sa @ref deadline_timer_reset
*
* @par Examples:
* Performing a blocking wait:
* @code
* // Construct a timer without setting an expiry time.
* asio::deadline_timer timer(io_service);
*
* // Set an expiry time relative to now.
* timer.expires_from_now(boost::posix_time::seconds(5));
*
* // Wait for the timer to expire.
* timer.wait();
* @endcode
*
* @par
* Performing an asynchronous wait:
* @code
* void handler(const asio::error& error)
* {
* if (!error)
* {
* // Timer expired.
* }
* }
*
* ...
*
* // Construct a timer with an absolute expiry time.
* asio::deadline_timer timer(io_service,
* boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
*
* // Start an asynchronous wait.
* timer.async_wait(handler);
* @endcode
*/
template <typename Time_Type,
typename Time_Traits = asio::time_traits<Time_Type>,
typename Service = deadline_timer_service<Time_Type, Time_Traits> >
class basic_deadline_timer
: public basic_io_object<Service>
{
public:
/// The type used for reporting errors.
typedef asio::error error_type;
/// The time traits type.
typedef Time_Traits traits_type;
/// The time type.
typedef typename traits_type::time_type time_type;
/// The duration type.
typedef typename traits_type::duration_type duration_type;
/// Constructor.
/**
* This constructor creates a timer without setting an expiry time. The
* expires_at() or expires_from_now() functions must be called to set an
* expiry time before the timer can be waited on.
*
* @param io_service The io_service object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*/
explicit basic_deadline_timer(asio::io_service& io_service)
: basic_io_object<Service>(io_service)
{
}
/// Constructor to set a particular expiry time as an absolute time.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_service The io_service object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
basic_deadline_timer(asio::io_service& io_service,
const time_type& expiry_time)
: basic_io_object<Service>(io_service)
{
this->service.expires_at(this->implementation, expiry_time);
}
/// Constructor to set a particular expiry time relative to now.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param io_service The io_service object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
basic_deadline_timer(asio::io_service& io_service,
const duration_type& expiry_time)
: basic_io_object<Service>(io_service)
{
this->service.expires_from_now(this->implementation, expiry_time);
}
/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled.
*/
std::size_t cancel()
{
return this->service.cancel(this->implementation);
}
/// Get the timer's expiry time as an absolute time.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
time_type expires_at() const
{
return this->service.expires_at(this->implementation);
}
/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* See @ref deadline_timer_reset for more information on altering the expiry
* time of an active timer.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*/
std::size_t expires_at(const time_type& expiry_time)
{
return this->service.expires_at(this->implementation, expiry_time);
}
/// Get the timer's expiry time relative to now.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
duration_type expires_from_now() const
{
return this->service.expires_from_now(this->implementation);
}
/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* See @ref deadline_timer_reset for more information on altering the expiry
* time of an active timer.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*/
std::size_t expires_from_now(const duration_type& expiry_time)
{
return this->service.expires_from_now(this->implementation, expiry_time);
}
/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @throws asio::error Thrown on failure.
*/
void wait()
{
this->service.wait(this->implementation);
}
/// Start an asynchronous wait on the timer.
/**
* This function may be used to initiate an asynchronous wait against the
* timer. It always returns immediately.
*
* For each call to async_wait(), the supplied handler will be called exactly
* once. The handler will be called when:
*
* @li The timer has expired.
*
* @li The timer was cancelled, in which case the handler is passed the error
* code asio::error::operation_aborted.
*
* @param handler The handler to be called when the timer expires. Copies
* will be made of the handler as required. The function signature of the
* handler must be:
* @code void handler(
* const asio::error& error // Result of operation
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*/
template <typename Handler>
void async_wait(Handler handler)
{
this->service.async_wait(this->implementation, handler);
}
};
/**
* @page deadline_timer_reset Changing an active deadline_timer's expiry time
*
* Changing the expiry time of a timer while there are pending asynchronous
* waits causes those wait operations to be cancelled. To ensure that the action
* associated with the timer is performed only once, use something like this:
* used:
*
* @code
* void on_some_event()
* {
* if (my_timer.expires_from_now(seconds(5)) > 0)
* {
* // We managed to cancel the timer. Start new asynchronous wait.
* my_timer.async_wait(on_timeout);
* }
* else
* {
* // Too late, timer has already expired!
* }
* }
*
* void on_timeout(const asio::error& e)
* {
* if (e != asio::error::operation_aborted)
* {
* // Timer was not cancelled, take necessary action.
* }
* }
* @endcode
*
* @li The asio::basic_deadline_timer::expires_from_now() function
* cancels any pending asynchronous waits, and returns the number of
* asynchronous waits that were cancelled. If it returns 0 then you were too
* late and the wait handler has already been executed, or will soon be
* executed. If it returns 1 then the wait handler was successfully cancelled.
*
* @li If a wait handler is cancelled, the asio::error passed to it
* contains the value asio::error::operation_aborted.
*
* @sa asio::basic_deadline_timer
*/
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_DEADLINE_TIMER_HPP

View File

@@ -0,0 +1,75 @@
//
// basic_io_object.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_IO_OBJECT_HPP
#define ASIO_BASIC_IO_OBJECT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
/// Base class for all I/O objects.
template <typename Service>
class basic_io_object
: private noncopyable
{
public:
/// The type of the service that will be used to provide I/O operations.
typedef Service service_type;
/// The underlying implementation type of I/O object.
typedef typename service_type::implementation_type implementation_type;
/// Get the io_service associated with the object.
/**
* This function may be used to obtain the io_service object that the I/O
* object uses to dispatch handlers for asynchronous operations.
*
* @return A reference to the io_service object that the I/O object will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
asio::io_service& io_service()
{
return service.io_service();
}
protected:
/// Construct a basic_io_object.
explicit basic_io_object(asio::io_service& io_service)
: service(asio::use_service<Service>(io_service))
{
service.construct(implementation);
}
/// Protected destructor to prevent deletion through this type.
~basic_io_object()
{
service.destroy(implementation);
}
// The backend service implementation.
service_type& service;
// The underlying native implementation.
implementation_type implementation;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_IO_OBJECT_HPP

View File

@@ -0,0 +1,252 @@
//
// basic_resolver.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_RESOLVER_HPP
#define ASIO_BASIC_RESOLVER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/error.hpp"
#include "asio/error_handler.hpp"
#include "asio/resolver_service.hpp"
namespace asio {
/// Provides endpoint resolution functionality.
/**
* The basic_resolver class template provides the ability to resolve a query
* to a list of endpoints.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Error_Source.
*/
template <typename Protocol, typename Service = resolver_service<Protocol> >
class basic_resolver
: public basic_io_object<Service>
{
public:
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// The query type.
typedef typename Protocol::resolver_query query;
/// The iterator type.
typedef typename Protocol::resolver_iterator iterator;
/// The type used for reporting errors.
typedef asio::error error_type;
/// Constructor.
/**
* This constructor creates a basic_resolver.
*
* @param io_service The io_service object that the resolver will use to
* dispatch handlers for any asynchronous operations performed on the timer.
*/
explicit basic_resolver(asio::io_service& io_service)
: basic_io_object<Service>(io_service)
{
}
/// Cancel any asynchronous operations that are waiting on the resolver.
/**
* This function forces the completion of any pending asynchronous
* operations on the host resolver. The handler for each cancelled operation
* will be invoked with the asio::error::operation_aborted error code.
*/
void cancel()
{
return this->service.cancel(this->implementation);
}
/// Resolve a query to a list of entries.
/**
* This function is used to resolve a query into a list of endpoint entries.
*
* @param q A query object that determines what endpoints will be returned.
*
* @returns A forward-only iterator that can be used to traverse the list
* of endpoint entries.
*
* @throws asio::error Thrown on failure.
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful call to this function is guaranteed to return at least
* one entry.
*/
iterator resolve(const query& q)
{
return this->service.resolve(this->implementation, q, throw_error());
}
/// Resolve a query to a list of entries.
/**
* This function is used to resolve a query into a list of endpoint entries.
*
* @param q A query object that determines what endpoints will be returned.
*
* @returns A forward-only iterator that can be used to traverse the list
* of endpoint entries. Returns a default constructed iterator if an error
* occurs.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful call to this function is guaranteed to return at least
* one entry.
*/
template <typename Error_Handler>
iterator resolve(const query& q, Error_Handler error_handler)
{
return this->service.resolve(this->implementation, q, error_handler);
}
/// Asynchronously resolve a query to a list of entries.
/**
* This function is used to asynchronously resolve a query into a list of
* endpoint entries.
*
* @param q A query object that determines what endpoints will be returned.
*
* @param handler The handler to be called when the resolve operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* resolver::iterator iterator // Forward-only iterator that can be used to
* // traverse the list of endpoint entries.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful resolve operation is guaranteed to pass at least one
* entry to the handler.
*/
template <typename Handler>
void async_resolve(const query& q, Handler handler)
{
return this->service.async_resolve(this->implementation, q, handler);
}
/// Resolve an endpoint to a list of entries.
/**
* This function is used to resolve an endpoint into a list of endpoint
* entries.
*
* @param e An endpoint object that determines what endpoints will be
* returned.
*
* @returns A forward-only iterator that can be used to traverse the list
* of endpoint entries.
*
* @throws asio::error Thrown on failure.
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful call to this function is guaranteed to return at least
* one entry.
*/
iterator resolve(const endpoint_type& e)
{
return this->service.resolve(this->implementation, e, throw_error());
}
/// Resolve an endpoint to a list of entries.
/**
* This function is used to resolve an endpoint into a list of endpoint
* entries.
*
* @param e An endpoint object that determines what endpoints will be
* returned.
*
* @returns A forward-only iterator that can be used to traverse the list
* of endpoint entries. Returns a default constructed iterator if an error
* occurs.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful call to this function is guaranteed to return at least
* one entry.
*/
template <typename Error_Handler>
iterator resolve(const endpoint_type& e, Error_Handler error_handler)
{
return this->service.resolve(this->implementation, e, error_handler);
}
/// Asynchronously resolve an endpoint to a list of entries.
/**
* This function is used to asynchronously resolve an endpoint into a list of
* endpoint entries.
*
* @param e An endpoint object that determines what endpoints will be
* returned.
*
* @param handler The handler to be called when the resolve operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* resolver::iterator iterator // Forward-only iterator that can be used to
* // traverse the list of endpoint entries.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note A default constructed iterator represents the end of the list.
*
* @note A successful resolve operation is guaranteed to pass at least one
* entry to the handler.
*/
template <typename Handler>
void async_resolve(const endpoint_type& e, Handler handler)
{
return this->service.async_resolve(this->implementation, e, handler);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_RESOLVER_HPP

View File

@@ -0,0 +1,919 @@
//
// basic_socket.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_SOCKET_HPP
#define ASIO_BASIC_SOCKET_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/error.hpp"
#include "asio/error_handler.hpp"
#include "asio/socket_base.hpp"
namespace asio {
/// Provides socket functionality.
/**
* The basic_socket class template provides functionality that is common to both
* stream-oriented and datagram-oriented sockets.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Error_Source, IO_Object.
*/
template <typename Protocol, typename Service>
class basic_socket
: public basic_io_object<Service>,
public socket_base
{
public:
/// The native representation of a socket.
typedef typename Service::native_type native_type;
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// The type used for reporting errors.
typedef asio::error error_type;
/// A basic_socket is always the lowest layer.
typedef basic_socket<Protocol, Service> lowest_layer_type;
/// Construct a basic_socket without opening it.
/**
* This constructor creates a socket without opening it.
*
* @param io_service The io_service object that the socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*/
explicit basic_socket(asio::io_service& io_service)
: basic_io_object<Service>(io_service)
{
}
/// Construct and open a basic_socket.
/**
* This constructor creates and opens a socket.
*
* @param io_service The io_service object that the socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::error Thrown on failure.
*/
basic_socket(asio::io_service& io_service,
const protocol_type& protocol)
: basic_io_object<Service>(io_service)
{
this->service.open(this->implementation, protocol, throw_error());
}
/// Construct a basic_socket, opening it and binding it to the given local
/// endpoint.
/**
* This constructor creates a socket and automatically opens it bound to the
* specified endpoint on the local machine. The protocol used is the protocol
* associated with the given endpoint.
*
* @param io_service The io_service object that the socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param endpoint An endpoint on the local machine to which the socket will
* be bound.
*
* @throws asio::error Thrown on failure.
*/
basic_socket(asio::io_service& io_service,
const endpoint_type& endpoint)
: basic_io_object<Service>(io_service)
{
this->service.open(this->implementation, endpoint.protocol(),
throw_error());
this->service.bind(this->implementation, endpoint, throw_error());
}
/// Construct a basic_socket on an existing native socket.
/**
* This constructor creates a socket object to hold an existing native socket.
*
* @param io_service The io_service object that the socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket A native socket.
*
* @throws asio::error Thrown on failure.
*/
basic_socket(asio::io_service& io_service,
const protocol_type& protocol, const native_type& native_socket)
: basic_io_object<Service>(io_service)
{
this->service.assign(this->implementation, protocol, native_socket,
throw_error());
}
/// Get a reference to the lowest layer.
/**
* This function returns a reference to the lowest layer in a stack of
* layers. Since a basic_socket cannot contain any further layers, it simply
* returns a reference to itself.
*
* @return A reference to the lowest layer in the stack of layers. Ownership
* is not transferred to the caller.
*/
lowest_layer_type& lowest_layer()
{
return *this;
}
/// Open the socket using the specified protocol.
/**
* This function opens the socket so that it will use the specified protocol.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* socket.open(asio::ip::tcp::v4());
* @endcode
*/
void open(const protocol_type& protocol = protocol_type())
{
this->service.open(this->implementation, protocol, throw_error());
}
/// Open the socket using the specified protocol.
/**
* This function opens the socket so that it will use the specified protocol.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* asio::error error;
* socket.open(asio::ip::tcp::v4(), asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void open(const protocol_type& protocol, Error_Handler error_handler)
{
this->service.open(this->implementation, protocol, error_handler);
}
/// Assign an existing native socket to the socket.
/*
* This function opens the socket to hold an existing native socket.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param native_socket A native socket.
*
* @throws asio::error Thrown on failure.
*/
void assign(const protocol_type& protocol, const native_type& native_socket)
{
this->service.assign(this->implementation, protocol, native_socket,
throw_error());
}
/// Assign an existing native socket to the socket.
/*
* This function opens the socket to hold an existing native socket.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param native_socket A native socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*/
template <typename Error_Handler>
void assign(const protocol_type& protocol, const native_type& native_socket,
Error_Handler error_handler)
{
this->service.assign(this->implementation, protocol, native_socket,
error_handler);
}
/// Close the socket.
/**
* This function is used to close the socket. Any asynchronous send, receive
* or connect operations will be cancelled immediately, and will complete
* with the asio::error::operation_aborted error.
*
* @throws asio::error Thrown on failure.
*/
void close()
{
this->service.close(this->implementation, throw_error());
}
/// Close the socket.
/**
* This function is used to close the socket. Any asynchronous send, receive
* or connect operations will be cancelled immediately, and will complete
* with the asio::error::operation_aborted error.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::error error;
* socket.close(asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void close(Error_Handler error_handler)
{
this->service.close(this->implementation, error_handler);
}
/// Get the native socket representation.
/**
* This function may be used to obtain the underlying representation of the
* socket. This is intended to allow access to native socket functionality
* that is not otherwise provided.
*/
native_type native()
{
return this->service.native(this->implementation);
}
/// Cancel all asynchronous operations associated with the socket.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error.
*
* @throws asio::error Thrown on failure.
*/
void cancel()
{
this->service.cancel(this->implementation, throw_error());
}
/// Cancel all asynchronous operations associated with the socket.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*/
template <typename Error_Handler>
void cancel(Error_Handler error_handler)
{
this->service.cancel(this->implementation, error_handler);
}
/// Bind the socket to the given local endpoint.
/**
* This function binds the socket to the specified endpoint on the local
* machine.
*
* @param endpoint An endpoint on the local machine to which the socket will
* be bound.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* socket.open(asio::ip::tcp::v4());
* socket.bind(asio::ip::tcp::endpoint(
* asio::ip::tcp::v4(), 12345));
* @endcode
*/
void bind(const endpoint_type& endpoint)
{
this->service.bind(this->implementation, endpoint, throw_error());
}
/// Bind the socket to the given local endpoint.
/**
* This function binds the socket to the specified endpoint on the local
* machine.
*
* @param endpoint An endpoint on the local machine to which the socket will
* be bound.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* socket.open(asio::ip::tcp::v4());
* asio::error error;
* socket.bind(asio::ip::tcp::endpoint(
* asio::ip::tcp::v4(), 12345),
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void bind(const endpoint_type& endpoint, Error_Handler error_handler)
{
this->service.bind(this->implementation, endpoint, error_handler);
}
/// Connect the socket to the specified endpoint.
/**
* This function is used to connect a socket to the specified remote endpoint.
* The function call will block until the connection is successfully made or
* an error occurs.
*
* The socket is automatically opened if it is not already open. If the
* connect fails, and the socket was automatically opened, the socket is
* returned to the closed state.
*
* @param peer_endpoint The remote endpoint to which the socket will be
* connected.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* asio::ip::tcp::endpoint endpoint(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.connect(endpoint);
* @endcode
*/
void connect(const endpoint_type& peer_endpoint)
{
this->service.connect(this->implementation, peer_endpoint, throw_error());
}
/// Connect the socket to the specified endpoint.
/**
* This function is used to connect a socket to the specified remote endpoint.
* The function call will block until the connection is successfully made or
* an error occurs.
*
* The socket is automatically opened if it is not already open. If the
* connect fails, and the socket was automatically opened, the socket is
* returned to the closed state.
*
* @param peer_endpoint The remote endpoint to which the socket will be
* connected.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* asio::ip::tcp::endpoint endpoint(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* asio::error error;
* socket.connect(endpoint, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void connect(const endpoint_type& peer_endpoint, Error_Handler error_handler)
{
this->service.connect(this->implementation, peer_endpoint, error_handler);
}
/// Start an asynchronous connect.
/**
* This function is used to asynchronously connect a socket to the specified
* remote endpoint. The function call always returns immediately.
*
* The socket is automatically opened if it is not already open. If the
* connect fails, and the socket was automatically opened, the socket is
* returned to the closed state.
*
* @param peer_endpoint The remote endpoint to which the socket will be
* connected. Copies will be made of the endpoint object as required.
*
* @param handler The handler to be called when the connection operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error // Result of operation
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @par Example:
* @code
* void connect_handler(const asio::error& error)
* {
* if (!error)
* {
* // Connect succeeded.
* }
* }
*
* ...
*
* asio::ip::tcp::socket socket(io_service);
* asio::ip::tcp::endpoint endpoint(
* asio::ip::address::from_string("1.2.3.4"), 12345);
* socket.async_connect(endpoint, connect_handler);
* @endcode
*/
template <typename Handler>
void async_connect(const endpoint_type& peer_endpoint, Handler handler)
{
this->service.async_connect(this->implementation, peer_endpoint, handler);
}
/// Set an option on the socket.
/**
* This function is used to set an option on the socket.
*
* @param option The new option value to be set on the socket.
*
* @throws asio::error Thrown on failure.
*
* @sa Socket_Option @n
* asio::socket_base::broadcast @n
* asio::socket_base::do_not_route @n
* asio::socket_base::keep_alive @n
* asio::socket_base::linger @n
* asio::socket_base::receive_buffer_size @n
* asio::socket_base::receive_low_watermark @n
* asio::socket_base::reuse_address @n
* asio::socket_base::send_buffer_size @n
* asio::socket_base::send_low_watermark @n
* asio::ip::multicast::join_group @n
* asio::ip::multicast::leave_group @n
* asio::ip::multicast::enable_loopback @n
* asio::ip::multicast::outbound_interface @n
* asio::ip::multicast::hops @n
* asio::ip::tcp::no_delay
*
* @par Example:
* Setting the IPPROTO_TCP/TCP_NODELAY option:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::no_delay option(true);
* socket.set_option(option);
* @endcode
*/
template <typename Socket_Option>
void set_option(const Socket_Option& option)
{
this->service.set_option(this->implementation, option, throw_error());
}
/// Set an option on the socket.
/**
* This function is used to set an option on the socket.
*
* @param option The new option value to be set on the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @sa Socket_Option @n
* asio::socket_base::broadcast @n
* asio::socket_base::do_not_route @n
* asio::socket_base::keep_alive @n
* asio::socket_base::linger @n
* asio::socket_base::receive_buffer_size @n
* asio::socket_base::receive_low_watermark @n
* asio::socket_base::reuse_address @n
* asio::socket_base::send_buffer_size @n
* asio::socket_base::send_low_watermark @n
* asio::ip::multicast::join_group @n
* asio::ip::multicast::leave_group @n
* asio::ip::multicast::enable_loopback @n
* asio::ip::multicast::outbound_interface @n
* asio::ip::multicast::hops @n
* asio::ip::tcp::no_delay
*
* @par Example:
* Setting the IPPROTO_TCP/TCP_NODELAY option:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::no_delay option(true);
* asio::error error;
* socket.set_option(option, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Socket_Option, typename Error_Handler>
void set_option(const Socket_Option& option, Error_Handler error_handler)
{
this->service.set_option(this->implementation, option, error_handler);
}
/// Get an option from the socket.
/**
* This function is used to get the current value of an option on the socket.
*
* @param option The option value to be obtained from the socket.
*
* @throws asio::error Thrown on failure.
*
* @sa Socket_Option @n
* asio::socket_base::broadcast @n
* asio::socket_base::do_not_route @n
* asio::socket_base::keep_alive @n
* asio::socket_base::linger @n
* asio::socket_base::receive_buffer_size @n
* asio::socket_base::receive_low_watermark @n
* asio::socket_base::reuse_address @n
* asio::socket_base::send_buffer_size @n
* asio::socket_base::send_low_watermark @n
* asio::ip::multicast::join_group @n
* asio::ip::multicast::leave_group @n
* asio::ip::multicast::enable_loopback @n
* asio::ip::multicast::outbound_interface @n
* asio::ip::multicast::hops @n
* asio::ip::tcp::no_delay
*
* @par Example:
* Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::socket::keep_alive option;
* socket.get_option(option);
* bool is_set = option.get();
* @endcode
*/
template <typename Socket_Option>
void get_option(Socket_Option& option) const
{
this->service.get_option(this->implementation, option, throw_error());
}
/// Get an option from the socket.
/**
* This function is used to get the current value of an option on the socket.
*
* @param option The option value to be obtained from the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @sa Socket_Option @n
* asio::socket_base::broadcast @n
* asio::socket_base::do_not_route @n
* asio::socket_base::keep_alive @n
* asio::socket_base::linger @n
* asio::socket_base::receive_buffer_size @n
* asio::socket_base::receive_low_watermark @n
* asio::socket_base::reuse_address @n
* asio::socket_base::send_buffer_size @n
* asio::socket_base::send_low_watermark @n
* asio::ip::multicast::join_group @n
* asio::ip::multicast::leave_group @n
* asio::ip::multicast::enable_loopback @n
* asio::ip::multicast::outbound_interface @n
* asio::ip::multicast::hops @n
* asio::ip::tcp::no_delay
*
* @par Example:
* Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::socket::keep_alive option;
* asio::error error;
* socket.get_option(option, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* bool is_set = option.get();
* @endcode
*/
template <typename Socket_Option, typename Error_Handler>
void get_option(Socket_Option& option, Error_Handler error_handler) const
{
this->service.get_option(this->implementation, option, error_handler);
}
/// Perform an IO control command on the socket.
/**
* This function is used to execute an IO control command on the socket.
*
* @param command The IO control command to be performed on the socket.
*
* @throws asio::error Thrown on failure.
*
* @sa IO_Control_Command @n
* asio::socket_base::bytes_readable @n
* asio::socket_base::non_blocking_io
*
* @par Example:
* Getting the number of bytes ready to read:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::socket::bytes_readable command;
* socket.io_control(command);
* std::size_t bytes_readable = command.get();
* @endcode
*/
template <typename IO_Control_Command>
void io_control(IO_Control_Command& command)
{
this->service.io_control(this->implementation, command, throw_error());
}
/// Perform an IO control command on the socket.
/**
* This function is used to execute an IO control command on the socket.
*
* @param command The IO control command to be performed on the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @sa IO_Control_Command @n
* asio::socket_base::bytes_readable @n
* asio::socket_base::non_blocking_io
*
* @par Example:
* Getting the number of bytes ready to read:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::socket::bytes_readable command;
* asio::error error;
* socket.io_control(command, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* std::size_t bytes_readable = command.get();
* @endcode
*/
template <typename IO_Control_Command, typename Error_Handler>
void io_control(IO_Control_Command& command, Error_Handler error_handler)
{
this->service.io_control(this->implementation, command, error_handler);
}
/// Get the local endpoint of the socket.
/**
* This function is used to obtain the locally bound endpoint of the socket.
*
* @returns An object that represents the local endpoint of the socket.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::endpoint endpoint = socket.local_endpoint();
* @endcode
*/
endpoint_type local_endpoint() const
{
return this->service.local_endpoint(this->implementation, throw_error());
}
/// Get the local endpoint of the socket.
/**
* This function is used to obtain the locally bound endpoint of the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @returns An object that represents the local endpoint of the socket.
* Returns a default-constructed endpoint object if an error occurred and the
* error handler did not throw an exception.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::error error;
* asio::ip::tcp::endpoint endpoint
* = socket.local_endpoint(asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
endpoint_type local_endpoint(Error_Handler error_handler) const
{
return this->service.local_endpoint(this->implementation, error_handler);
}
/// Get the remote endpoint of the socket.
/**
* This function is used to obtain the remote endpoint of the socket.
*
* @returns An object that represents the remote endpoint of the socket.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::ip::tcp::endpoint endpoint = socket.remote_endpoint();
* @endcode
*/
endpoint_type remote_endpoint() const
{
return this->service.remote_endpoint(this->implementation, throw_error());
}
/// Get the remote endpoint of the socket.
/**
* This function is used to obtain the remote endpoint of the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @returns An object that represents the remote endpoint of the socket.
* Returns a default-constructed endpoint object if an error occurred and the
* error handler did not throw an exception.
*
* @par Example:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::error error;
* asio::ip::tcp::endpoint endpoint
* = socket.remote_endpoint(asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
endpoint_type remote_endpoint(Error_Handler error_handler) const
{
return this->service.remote_endpoint(this->implementation, error_handler);
}
/// Disable sends or receives on the socket.
/**
* This function is used to disable send operations, receive operations, or
* both.
*
* @param what Determines what types of operation will no longer be allowed.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* Shutting down the send side of the socket:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* socket.shutdown(asio::ip::tcp::socket::shutdown_send);
* @endcode
*/
void shutdown(shutdown_type what)
{
this->service.shutdown(this->implementation, what, throw_error());
}
/// Disable sends or receives on the socket.
/**
* This function is used to disable send operations, receive operations, or
* both.
*
* @param what Determines what types of operation will no longer be allowed.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* Shutting down the send side of the socket:
* @code
* asio::ip::tcp::socket socket(io_service);
* ...
* asio::error error;
* socket.shutdown(asio::ip::tcp::socket::shutdown_send,
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void shutdown(shutdown_type what, Error_Handler error_handler)
{
this->service.shutdown(this->implementation, what, error_handler);
}
protected:
/// Protected destructor to prevent deletion through this type.
~basic_socket()
{
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_SOCKET_HPP

View File

@@ -0,0 +1,854 @@
//
// basic_socket_acceptor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP
#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/basic_socket.hpp"
#include "asio/error.hpp"
#include "asio/error_handler.hpp"
#include "asio/socket_acceptor_service.hpp"
#include "asio/socket_base.hpp"
namespace asio {
/// Provides the ability to accept new connections.
/**
* The basic_socket_acceptor class template is used for accepting new socket
* connections.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Error_Source.
*
* @par Example:
* Opening a socket acceptor with the SO_REUSEADDR option enabled:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
* acceptor.open(endpoint.protocol());
* acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
* acceptor.bind(endpoint);
* acceptor.listen();
* @endcode
*/
template <typename Protocol,
typename Service = socket_acceptor_service<Protocol> >
class basic_socket_acceptor
: public basic_io_object<Service>,
public socket_base
{
public:
/// The native representation of an acceptor.
typedef typename Service::native_type native_type;
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// The type used for reporting errors.
typedef asio::error error_type;
/// Construct an acceptor without opening it.
/**
* This constructor creates an acceptor without opening it to listen for new
* connections. The open() function must be called before the acceptor can
* accept new socket connections.
*
* @param io_service The io_service object that the acceptor will use to
* dispatch handlers for any asynchronous operations performed on the
* acceptor.
*/
explicit basic_socket_acceptor(asio::io_service& io_service)
: basic_io_object<Service>(io_service)
{
}
/// Construct an open acceptor.
/**
* This constructor creates an acceptor and automatically opens it.
*
* @param io_service The io_service object that the acceptor will use to
* dispatch handlers for any asynchronous operations performed on the
* acceptor.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::error Thrown on failure.
*/
basic_socket_acceptor(asio::io_service& io_service,
const protocol_type& protocol)
: basic_io_object<Service>(io_service)
{
this->service.open(this->implementation, protocol, throw_error());
}
/// Construct an acceptor opened on the given endpoint.
/**
* This constructor creates an acceptor and automatically opens it to listen
* for new connections on the specified endpoint.
*
* @param io_service The io_service object that the acceptor will use to
* dispatch handlers for any asynchronous operations performed on the
* acceptor.
*
* @param endpoint An endpoint on the local machine on which the acceptor
* will listen for new connections.
*
* @param reuse_addr Whether the constructor should set the socket option
* socket_base::reuse_address.
*
* @throws asio::error Thrown on failure.
*
* @note This constructor is equivalent to the following code:
* @code
* basic_socket_acceptor<Protocol> acceptor(io_service);
* acceptor.open(endpoint.protocol());
* if (reuse_addr)
* acceptor.set_option(socket_base::reuse_address(true));
* acceptor.bind(endpoint);
* acceptor.listen(listen_backlog);
* @endcode
*/
basic_socket_acceptor(asio::io_service& io_service,
const endpoint_type& endpoint, bool reuse_addr = true)
: basic_io_object<Service>(io_service)
{
this->service.open(this->implementation, endpoint.protocol(),
throw_error());
if (reuse_addr)
{
this->service.set_option(this->implementation,
socket_base::reuse_address(true), throw_error());
}
this->service.bind(this->implementation, endpoint, throw_error());
this->service.listen(this->implementation,
socket_base::max_connections, throw_error());
}
/// Construct a basic_socket_acceptor on an existing native acceptor.
/**
* This constructor creates an acceptor object to hold an existing native
* acceptor.
*
* @param io_service The io_service object that the acceptor will use to
* dispatch handlers for any asynchronous operations performed on the
* acceptor.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_acceptor A native acceptor.
*
* @throws asio::error Thrown on failure.
*/
basic_socket_acceptor(asio::io_service& io_service,
const protocol_type& protocol, const native_type& native_acceptor)
: basic_io_object<Service>(io_service)
{
this->service.assign(this->implementation, protocol, native_acceptor,
throw_error());
}
/// Open the acceptor using the specified protocol.
/**
* This function opens the socket acceptor so that it will use the specified
* protocol.
*
* @param protocol An object specifying which protocol is to be used.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* acceptor.open(asio::ip::tcp::v4());
* @endcode
*/
void open(const protocol_type& protocol = protocol_type())
{
this->service.open(this->implementation, protocol, throw_error());
}
/// Open the acceptor using the specified protocol.
/**
* This function opens the socket acceptor so that it will use the specified
* protocol.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* asio::error error;
* acceptor.open(asio::ip::tcp::v4(),
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void open(const protocol_type& protocol, Error_Handler error_handler)
{
this->service.open(this->implementation, protocol, error_handler);
}
/// Assigns an existing native acceptor to the acceptor.
/*
* This function opens the acceptor to hold an existing native acceptor.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param native_acceptor A native acceptor.
*
* @throws asio::error Thrown on failure.
*/
void assign(const protocol_type& protocol, const native_type& native_acceptor)
{
this->service.assign(this->implementation, protocol, native_acceptor,
throw_error());
}
/// Assigns an existing native acceptor to the acceptor.
/*
* This function opens the acceptor to hold an existing native acceptor.
*
* @param protocol An object specifying which protocol is to be used.
*
* @param native_acceptor A native acceptor.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*/
template <typename Error_Handler>
void assign(const protocol_type& protocol, const native_type& native_acceptor,
Error_Handler error_handler)
{
this->service.assign(this->implementation, protocol, native_acceptor,
error_handler);
}
/// Bind the acceptor to the given local endpoint.
/**
* This function binds the socket acceptor to the specified endpoint on the
* local machine.
*
* @param endpoint An endpoint on the local machine to which the socket
* acceptor will be bound.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* acceptor.open(asio::ip::tcp::v4());
* acceptor.bind(asio::ip::tcp::endpoint(12345));
* @endcode
*/
void bind(const endpoint_type& endpoint)
{
this->service.bind(this->implementation, endpoint, throw_error());
}
/// Bind the acceptor to the given local endpoint.
/**
* This function binds the socket acceptor to the specified endpoint on the
* local machine.
*
* @param endpoint An endpoint on the local machine to which the socket
* acceptor will be bound.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* acceptor.open(asio::ip::tcp::v4());
* asio::error error;
* acceptor.bind(asio::ip::tcp::endpoint(12345),
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void bind(const endpoint_type& endpoint, Error_Handler error_handler)
{
this->service.bind(this->implementation, endpoint, error_handler);
}
/// Place the acceptor into the state where it will listen for new
/// connections.
/**
* This function puts the socket acceptor into the state where it may accept
* new connections.
*
* @param backlog The maximum length of the queue of pending connections.
*/
void listen(int backlog = socket_base::max_connections)
{
this->service.listen(this->implementation, backlog, throw_error());
}
/// Place the acceptor into the state where it will listen for new
/// connections.
/**
* This function puts the socket acceptor into the state where it may accept
* new connections.
*
* @param backlog The maximum length of the queue of pending connections.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::error error;
* acceptor.listen(asio::socket_base::max_connections,
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void listen(int backlog, Error_Handler error_handler)
{
this->service.listen(this->implementation, backlog, error_handler);
}
/// Close the acceptor.
/**
* This function is used to close the acceptor. Any asynchronous accept
* operations will be cancelled immediately.
*
* A subsequent call to open() is required before the acceptor can again be
* used to again perform socket accept operations.
*
* @throws asio::error Thrown on failure.
*/
void close()
{
this->service.close(this->implementation, throw_error());
}
/// Close the acceptor.
/**
* This function is used to close the acceptor. Any asynchronous accept
* operations will be cancelled immediately.
*
* A subsequent call to open() is required before the acceptor can again be
* used to again perform socket accept operations.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::error error;
* acceptor.close(asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
void close(Error_Handler error_handler)
{
this->service.close(this->implementation, error_handler);
}
/// Get the native acceptor representation.
/**
* This function may be used to obtain the underlying representation of the
* acceptor. This is intended to allow access to native acceptor functionality
* that is not otherwise provided.
*/
native_type native()
{
return this->service.native(this->implementation);
}
/// Cancel all asynchronous operations associated with the acceptor.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error.
*
* @throws asio::error Thrown on failure.
*/
void cancel()
{
this->service.cancel(this->implementation, throw_error());
}
/// Cancel all asynchronous operations associated with the acceptor.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*/
template <typename Error_Handler>
void cancel(Error_Handler error_handler)
{
this->service.cancel(this->implementation, error_handler);
}
/// Set an option on the acceptor.
/**
* This function is used to set an option on the acceptor.
*
* @param option The new option value to be set on the acceptor.
*
* @throws asio::error Thrown on failure.
*
* @sa Socket_Option @n
* asio::socket_base::reuse_address
* asio::socket_base::enable_connection_aborted
*
* @par Example:
* Setting the SOL_SOCKET/SO_REUSEADDR option:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::acceptor::reuse_address option(true);
* acceptor.set_option(option);
* @endcode
*/
template <typename Option>
void set_option(const Option& option)
{
this->service.set_option(this->implementation, option, throw_error());
}
/// Set an option on the acceptor.
/**
* This function is used to set an option on the acceptor.
*
* @param option The new option value to be set on the acceptor.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @sa Socket_Option @n
* asio::socket_base::reuse_address
* asio::socket_base::enable_connection_aborted
*
* @par Example:
* Setting the SOL_SOCKET/SO_REUSEADDR option:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::acceptor::reuse_address option(true);
* asio::error error;
* acceptor.set_option(option, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Option, typename Error_Handler>
void set_option(const Option& option, Error_Handler error_handler)
{
this->service.set_option(this->implementation, option, error_handler);
}
/// Get an option from the acceptor.
/**
* This function is used to get the current value of an option on the
* acceptor.
*
* @param option The option value to be obtained from the acceptor.
*
* @throws asio::error Thrown on failure.
*
* @sa Socket_Option @n
* asio::socket_base::reuse_address
*
* @par Example:
* Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::acceptor::reuse_address option;
* acceptor.get_option(option);
* bool is_set = option.get();
* @endcode
*/
template <typename Option>
void get_option(Option& option)
{
this->service.get_option(this->implementation, option, throw_error());
}
/// Get an option from the acceptor.
/**
* This function is used to get the current value of an option on the
* acceptor.
*
* @param option The option value to be obtained from the acceptor.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @sa Socket_Option @n
* asio::socket_base::reuse_address
*
* @par Example:
* Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::acceptor::reuse_address option;
* asio::error error;
* acceptor.get_option(option, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* bool is_set = option.get();
* @endcode
*/
template <typename Option, typename Error_Handler>
void get_option(Option& option, Error_Handler error_handler)
{
this->service.get_option(this->implementation, option, error_handler);
}
/// Get the local endpoint of the acceptor.
/**
* This function is used to obtain the locally bound endpoint of the acceptor.
*
* @returns An object that represents the local endpoint of the acceptor.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint();
* @endcode
*/
endpoint_type local_endpoint() const
{
return this->service.local_endpoint(this->implementation, throw_error());
}
/// Get the local endpoint of the acceptor.
/**
* This function is used to obtain the locally bound endpoint of the acceptor.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @returns An object that represents the local endpoint of the acceptor.
* Returns a default-constructed endpoint object if an error occurred and the
* error handler did not throw an exception.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::error error;
* asio::ip::tcp::endpoint endpoint
* = acceptor.local_endpoint(asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Error_Handler>
endpoint_type local_endpoint(Error_Handler error_handler) const
{
return this->service.local_endpoint(this->implementation, error_handler);
}
/// Accept a new connection.
/**
* This function is used to accept a new connection from a peer into the
* given socket. The function call will block until a new connection has been
* accepted successfully or an error occurs.
*
* @param peer The socket into which the new connection will be accepted.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(io_service);
* acceptor.accept(socket);
* @endcode
*/
template <typename Socket_Service>
void accept(basic_socket<protocol_type, Socket_Service>& peer)
{
this->service.accept(this->implementation, peer, throw_error());
}
/// Accept a new connection.
/**
* This function is used to accept a new connection from a peer into the
* given socket. The function call will block until a new connection has been
* accepted successfully or an error occurs.
*
* @param peer The socket into which the new connection will be accepted.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::soocket socket(io_service);
* asio::error error;
* acceptor.accept(socket, asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Socket_Service, typename Error_Handler>
void accept(basic_socket<protocol_type, Socket_Service>& peer,
Error_Handler error_handler)
{
this->service.accept(this->implementation, peer, error_handler);
}
/// Start an asynchronous accept.
/**
* This function is used to asynchronously accept a new connection into a
* socket. The function call always returns immediately.
*
* @param peer The socket into which the new connection will be accepted.
* Ownership of the peer object is retained by the caller, which must
* guarantee that it is valid until the handler is called.
*
* @param handler The handler to be called when the accept operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error // Result of operation
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @par Example:
* @code
* void accept_handler(const asio::error& error)
* {
* if (!error)
* {
* // Accept succeeded.
* }
* }
*
* ...
*
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(io_service);
* acceptor.async_accept(socket, accept_handler);
* @endcode
*/
template <typename Socket_Service, typename Handler>
void async_accept(basic_socket<protocol_type, Socket_Service>& peer,
Handler handler)
{
this->service.async_accept(this->implementation, peer, handler);
}
/// Accept a new connection and obtain the endpoint of the peer
/**
* This function is used to accept a new connection from a peer into the
* given socket, and additionally provide the endpoint of the remote peer.
* The function call will block until a new connection has been accepted
* successfully or an error occurs.
*
* @param peer The socket into which the new connection will be accepted.
*
* @param peer_endpoint An endpoint object which will receive the endpoint of
* the remote peer.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(io_service);
* asio::ip::tcp::endpoint endpoint;
* acceptor.accept_endpoint(socket, endpoint);
* @endcode
*/
template <typename Socket_Service>
void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
endpoint_type& peer_endpoint)
{
this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
throw_error());
}
/// Accept a new connection and obtain the endpoint of the peer
/**
* This function is used to accept a new connection from a peer into the
* given socket, and additionally provide the endpoint of the remote peer.
* The function call will block until a new connection has been accepted
* successfully or an error occurs.
*
* @param peer The socket into which the new connection will be accepted.
*
* @param peer_endpoint An endpoint object which will receive the endpoint of
* the remote peer.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @par Example:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(io_service);
* asio::ip::tcp::endpoint endpoint;
* asio::error error;
* acceptor.accept_endpoint(socket, endpoint,
* asio::assign_error(error));
* if (error)
* {
* // An error occurred.
* }
* @endcode
*/
template <typename Socket_Service, typename Error_Handler>
void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
endpoint_type& peer_endpoint, Error_Handler error_handler)
{
this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
error_handler);
}
/// Start an asynchronous accept.
/**
* This function is used to asynchronously accept a new connection into a
* socket, and additionally obtain the endpoint of the remote peer. The
* function call always returns immediately.
*
* @param peer The socket into which the new connection will be accepted.
* Ownership of the peer object is retained by the caller, which must
* guarantee that it is valid until the handler is called.
*
* @param peer_endpoint An endpoint object into which the endpoint of the
* remote peer will be written. Ownership of the peer_endpoint object is
* retained by the caller, which must guarantee that it is valid until the
* handler is called.
*
* @param handler The handler to be called when the accept operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error // Result of operation
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*/
template <typename Socket_Service, typename Handler>
void async_accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
endpoint_type& peer_endpoint, Handler handler)
{
this->service.async_accept_endpoint(this->implementation, peer,
peer_endpoint, handler);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP

View File

@@ -0,0 +1,176 @@
//
// basic_socket_iostream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP
#define ASIO_BASIC_SOCKET_IOSTREAM_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/utility/base_from_member.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_socket_streambuf.hpp"
#include "asio/stream_socket_service.hpp"
#if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
#define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5
#endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
// A macro that should expand to:
// template < typename T1, ..., typename Tn >
// explicit basic_socket_iostream( T1 x1, ..., Tn xn )
// : basic_iostream<char>(&this->boost::base_from_member<
// basic_socket_streambuf<Protocol, Service> >::member)
// {
// try
// {
// rdbuf()->connect ( x1, ..., xn );
// }
// catch (asio::error&)
// {
// this->setstate(std::ios_base::failbit);
// if (this->exceptions() & std::ios_base::failbit)
// throw;
// }
// }
// This macro should only persist within this file.
#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
explicit basic_socket_iostream( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
: std::basic_iostream<char>(&this->boost::base_from_member< \
basic_socket_streambuf<Protocol, Service> >::member) \
{ \
try \
{ \
rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
} \
catch (asio::error&) \
{ \
this->setstate(std::ios_base::failbit); \
if (this->exceptions() & std::ios_base::failbit) \
throw; \
} \
} \
/**/
// A macro that should expand to:
// template < typename T1, ..., typename Tn >
// void connect( T1 x1, ..., Tn xn )
// {
// try
// {
// rdbuf()->connect ( x1, ..., xn );
// }
// catch (asio::error&)
// {
// this->setstate(std::ios_base::failbit);
// if (this->exceptions() & std::ios_base::failbit)
// throw;
// }
// }
// This macro should only persist within this file.
#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
{ \
try \
{ \
rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
} \
catch (asio::error&) \
{ \
this->setstate(std::ios_base::failbit); \
if (this->exceptions() & std::ios_base::failbit) \
throw; \
} \
} \
/**/
namespace asio {
/// Iostream interface for a socket.
template <typename Protocol,
typename Service = stream_socket_service<Protocol> >
class basic_socket_iostream
: public boost::base_from_member<basic_socket_streambuf<Protocol, Service> >,
public std::basic_iostream<char>
{
public:
/// Construct a basic_socket_iostream without establishing a connection.
basic_socket_iostream()
: std::basic_iostream<char>(&this->boost::base_from_member<
basic_socket_streambuf<Protocol, Service> >::member)
{
}
#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This constructor automatically establishes a connection based on the
* supplied resolver query parameters. The arguments are used to construct
* a resolver query object.
*/
template <typename T1, ..., typename TN>
explicit basic_socket_iostream(T1 t1, ..., TN tn);
#else
BOOST_PP_REPEAT_FROM_TO(
1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
ASIO_PRIVATE_CTR_DEF, _ )
#endif
#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This function automatically establishes a connection based on the supplied
* resolver query parameters. The arguments are used to construct a resolver
* query object.
*/
template <typename T1, ..., typename TN>
void connect(T1 t1, ..., TN tn);
#else
BOOST_PP_REPEAT_FROM_TO(
1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
ASIO_PRIVATE_CONNECT_DEF, _ )
#endif
/// Close the connection.
void close()
{
rdbuf()->close();
}
/// Return a pointer to the underlying streambuf.
basic_socket_streambuf<Protocol, Service>* rdbuf() const
{
return const_cast<basic_socket_streambuf<Protocol, Service>*>(
&this->boost::base_from_member<
basic_socket_streambuf<Protocol, Service> >::member);
}
};
} // namespace asio
#undef ASIO_PRIVATE_CTR_DEF
#undef ASIO_PRIVATE_CONNECT_DEF
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP

View File

@@ -0,0 +1,280 @@
//
// basic_socket_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
#define ASIO_BASIC_SOCKET_STREAMBUF_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <streambuf>
#include <boost/array.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/utility/base_from_member.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_socket.hpp"
#include "asio/error_handler.hpp"
#include "asio/io_service.hpp"
#include "asio/stream_socket_service.hpp"
#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5
#endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
// A macro that should expand to:
// template < typename T1, ..., typename Tn >
// explicit basic_socket_streambuf( T1 x1, ..., Tn xn )
// : basic_socket<Protocol, Service>(
// boost::base_from_member<io_service>::member)
// {
// init_buffers();
// typedef typename Protocol::resolver_query resolver_query;
// resolver_query query( x1, ..., xn );
// resolve_and_connect(query);
// }
// This macro should only persist within this file.
#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
explicit basic_socket_streambuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
: basic_socket<Protocol, Service>( \
boost::base_from_member<io_service>::member) \
{ \
init_buffers(); \
typedef typename Protocol::resolver_query resolver_query; \
resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
resolve_and_connect(query); \
} \
/**/
// A macro that should expand to:
// template < typename T1, ..., typename Tn >
// void connect( T1 x1, ..., Tn xn )
// {
// this->basic_socket<Protocol, Service>::close();
// init_buffers();
// typedef typename Protocol::resolver_query resolver_query;
// resolver_query query( x1, ..., xn );
// resolve_and_connect(query);
// }
// This macro should only persist within this file.
#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
{ \
this->basic_socket<Protocol, Service>::close(); \
init_buffers(); \
typedef typename Protocol::resolver_query resolver_query; \
resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
resolve_and_connect(query); \
} \
/**/
namespace asio {
/// Iostream streambuf for a socket.
template <typename Protocol,
typename Service = stream_socket_service<Protocol> >
class basic_socket_streambuf
: public std::streambuf,
private boost::base_from_member<io_service>,
public basic_socket<Protocol, Service>
{
public:
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// Construct a basic_socket_streambuf without establishing a connection.
basic_socket_streambuf()
: basic_socket<Protocol, Service>(
boost::base_from_member<asio::io_service>::member)
{
init_buffers();
}
/// Establish a connection to the specified endpoint.
explicit basic_socket_streambuf(const endpoint_type& endpoint)
: basic_socket<Protocol, Service>(
boost::base_from_member<asio::io_service>::member)
{
init_buffers();
this->basic_socket<Protocol, Service>::connect(endpoint);
}
#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This constructor automatically establishes a connection based on the
* supplied resolver query parameters. The arguments are used to construct
* a resolver query object.
*/
template <typename T1, ..., typename TN>
explicit basic_socket_streambuf(T1 t1, ..., TN tn);
#else
BOOST_PP_REPEAT_FROM_TO(
1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
ASIO_PRIVATE_CTR_DEF, _ )
#endif
/// Destructor flushes buffered data.
~basic_socket_streambuf()
{
sync();
}
/// Establish a connection to the specified endpoint.
void connect(const endpoint_type& endpoint)
{
this->basic_socket<Protocol, Service>::close();
init_buffers();
this->basic_socket<Protocol, Service>::connect(endpoint);
}
#if defined(GENERATING_DOCUMENTATION)
/// Establish a connection to an endpoint corresponding to a resolver query.
/**
* This function automatically establishes a connection based on the supplied
* resolver query parameters. The arguments are used to construct a resolver
* query object.
*/
template <typename T1, ..., typename TN>
void connect(T1 t1, ..., TN tn);
#else
BOOST_PP_REPEAT_FROM_TO(
1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
ASIO_PRIVATE_CONNECT_DEF, _ )
#endif
/// Close the connection.
void close()
{
sync();
this->basic_socket<Protocol, Service>::close();
init_buffers();
}
protected:
int_type underflow()
{
if (gptr() == egptr())
{
asio::error error;
std::size_t bytes_transferred = this->service.receive(
this->implementation,
asio::buffer(asio::buffer(get_buffer_) + putback_max),
0, asio::assign_error(error));
if (error)
{
if (error != asio::error::eof)
throw error;
return traits_type::eof();
}
setg(get_buffer_.begin(), get_buffer_.begin() + putback_max,
get_buffer_.begin() + putback_max + bytes_transferred);
return traits_type::to_int_type(*gptr());
}
else
{
return traits_type::eof();
}
}
int_type overflow(int_type c)
{
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
if (pptr() == epptr())
{
asio::const_buffer buffer =
asio::buffer(pbase(), pptr() - pbase());
while (asio::buffer_size(buffer) > 0)
{
std::size_t bytes_transferred = this->service.send(
this->implementation, asio::buffer(buffer),
0, asio::throw_error());
buffer = buffer + bytes_transferred;
}
setp(put_buffer_.begin(), put_buffer_.end());
}
*pptr() = traits_type::to_char_type(c);
pbump(1);
return c;
}
return traits_type::not_eof(c);
}
int sync()
{
asio::const_buffer buffer =
asio::buffer(pbase(), pptr() - pbase());
while (asio::buffer_size(buffer) > 0)
{
std::size_t bytes_transferred = this->service.send(
this->implementation, asio::buffer(buffer),
0, asio::throw_error());
buffer = buffer + bytes_transferred;
}
setp(put_buffer_.begin(), put_buffer_.end());
return 0;
}
private:
void init_buffers()
{
setg(get_buffer_.begin(),
get_buffer_.begin() + putback_max,
get_buffer_.begin() + putback_max);
setp(put_buffer_.begin(), put_buffer_.end());
}
void resolve_and_connect(const typename Protocol::resolver_query& query)
{
typedef typename Protocol::resolver resolver_type;
typedef typename Protocol::resolver_iterator iterator_type;
resolver_type resolver(
boost::base_from_member<asio::io_service>::member);
iterator_type iterator = resolver.resolve(query);
asio::error error(asio::error::host_not_found);
while (error && iterator != iterator_type())
{
this->basic_socket<Protocol, Service>::close();
this->basic_socket<Protocol, Service>::connect(
*iterator, asio::assign_error(error));
++iterator;
}
if (error)
throw error;
}
enum { putback_max = 8 };
enum { buffer_size = 512 };
boost::array<char, buffer_size> get_buffer_;
boost::array<char, buffer_size> put_buffer_;
};
} // namespace asio
#undef ASIO_PRIVATE_CTR_DEF
#undef ASIO_PRIVATE_CONNECT_DEF
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP

View File

@@ -0,0 +1,816 @@
//
// basic_stream_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_STREAM_SOCKET_HPP
#define ASIO_BASIC_STREAM_SOCKET_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_socket.hpp"
#include "asio/error_handler.hpp"
#include "asio/stream_socket_service.hpp"
namespace asio {
/// Provides stream-oriented socket functionality.
/**
* The basic_stream_socket class template provides asynchronous and blocking
* stream-oriented socket functionality.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Read_Stream, Async_Write_Stream, Error_Source, IO_Object, Stream,
* Sync_Read_Stream, Sync_Write_Stream.
*/
template <typename Protocol,
typename Service = stream_socket_service<Protocol> >
class basic_stream_socket
: public basic_socket<Protocol, Service>
{
public:
/// The native representation of a socket.
typedef typename Service::native_type native_type;
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
/// Construct a basic_stream_socket without opening it.
/**
* This constructor creates a stream socket without opening it. The socket
* needs to be opened and then connected or accepted before data can be sent
* or received on it.
*
* @param io_service The io_service object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*/
explicit basic_stream_socket(asio::io_service& io_service)
: basic_socket<Protocol, Service>(io_service)
{
}
/// Construct and open a basic_stream_socket.
/**
* This constructor creates and opens a stream socket. The socket needs to be
* connected or accepted before data can be sent or received on it.
*
* @param io_service The io_service object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @throws asio::error Thrown on failure.
*/
basic_stream_socket(asio::io_service& io_service,
const protocol_type& protocol)
: basic_socket<Protocol, Service>(io_service, protocol)
{
}
/// Construct a basic_stream_socket, opening it and binding it to the given
/// local endpoint.
/**
* This constructor creates a stream socket and automatically opens it bound
* to the specified endpoint on the local machine. The protocol used is the
* protocol associated with the given endpoint.
*
* @param io_service The io_service object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param endpoint An endpoint on the local machine to which the stream
* socket will be bound.
*
* @throws asio::error Thrown on failure.
*/
basic_stream_socket(asio::io_service& io_service,
const endpoint_type& endpoint)
: basic_socket<Protocol, Service>(io_service, endpoint)
{
}
/// Construct a basic_stream_socket on an existing native socket.
/**
* This constructor creates a stream socket object to hold an existing native
* socket.
*
* @param io_service The io_service object that the stream socket will use to
* dispatch handlers for any asynchronous operations performed on the socket.
*
* @param protocol An object specifying protocol parameters to be used.
*
* @param native_socket The new underlying socket implementation.
*
* @throws asio::error Thrown on failure.
*/
basic_stream_socket(asio::io_service& io_service,
const protocol_type& protocol, const native_type& native_socket)
: basic_socket<Protocol, Service>(io_service, protocol, native_socket)
{
}
/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.send(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers>
std::size_t send(const Const_Buffers& buffers)
{
return this->service.send(this->implementation, buffers, 0, throw_error());
}
/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @returns The number of bytes sent.
*
* @throws asio::error Thrown on failure.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.send(asio::buffer(data, size), 0);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers>
std::size_t send(const Const_Buffers& buffers,
socket_base::message_flags flags)
{
return this->service.send(this->implementation, buffers, flags,
throw_error());
}
/// Send some data on the socket.
/**
* This function is used to send data on the stream socket. The function
* call will block until one or more bytes of the data has been sent
* successfully, or an until error occurs.
*
* @param buffers One or more data buffers to be sent on the socket.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes sent. Returns 0 if an error occurred and the
* error handler did not throw an exception.
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref write function if you need to ensure that all data
* is written before the blocking operation completes.
*/
template <typename Const_Buffers, typename Error_Handler>
std::size_t send(const Const_Buffers& buffers,
socket_base::message_flags flags,
Error_Handler error_handler)
{
return this->service.send(this->implementation, buffers, flags,
error_handler);
}
/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers, typename Handler>
void async_send(const Const_Buffers& buffers, Handler handler)
{
this->service.async_send(this->implementation, buffers, 0, handler);
}
/// Start an asynchronous send.
/**
* This function is used to asynchronously send data on the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be sent on the socket. Although
* the buffers object may be copied as necessary, ownership of the underlying
* memory blocks is retained by the caller, which must guarantee that they
* remain valid until the handler is called.
*
* @param flags Flags specifying how the send call is to be made.
*
* @param handler The handler to be called when the send operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes sent.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The send operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example:
* To send a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_send(asio::buffer(data, size), 0, handler);
* @endcode
* See the @ref buffer documentation for information on sending multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers, typename Handler>
void async_send(const Const_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
this->service.async_send(this->implementation, buffers, flags, handler);
}
/// Receive some data on the socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t receive(const Mutable_Buffers& buffers)
{
return this->service.receive(this->implementation, buffers, 0,
throw_error());
}
/// Receive some data on the socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @returns The number of bytes received.
*
* @throws asio::error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.receive(asio::buffer(data, size), 0);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags)
{
return this->service.receive(this->implementation, buffers, flags,
throw_error());
}
/// Receive some data on a connected socket.
/**
* This function is used to receive data on the stream socket. The function
* call will block until one or more bytes of data has been received
* successfully, or until an error occurs.
*
* @param buffers One or more buffers into which the data will be received.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @returns The number of bytes received. Returns 0 if an error occurred and
* the error handler did not throw an exception.
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that the
* requested amount of data is read before the blocking operation completes.
*/
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags, Error_Handler error_handler)
{
return this->service.receive(this->implementation, buffers, flags,
error_handler);
}
/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the stream
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref async_read function if you need to ensure
* that the requested amount of data is received before the asynchronous
* operation completes.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive(const Mutable_Buffers& buffers, Handler handler)
{
this->service.async_receive(this->implementation, buffers, 0, handler);
}
/// Start an asynchronous receive.
/**
* This function is used to asynchronously receive data from the stream
* socket. The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be received.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param flags Flags specifying how the receive call is to be made.
*
* @param handler The handler to be called when the receive operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes received.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The receive operation may not receive all of the requested number of
* bytes. Consider using the @ref async_read function if you need to ensure
* that the requested amount of data is received before the asynchronous
* operation completes.
*
* @par Example:
* To receive into a single data buffer use the @ref buffer function as
* follows:
* @code
* socket.async_receive(asio::buffer(data, size), 0, handler);
* @endcode
* See the @ref buffer documentation for information on receiving into
* multiple buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers, typename Handler>
void async_receive(const Mutable_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
this->service.async_receive(this->implementation, buffers, flags, handler);
}
/// Write some data to the socket.
/**
* This function is used to write data to the stream socket. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the socket.
*
* @returns The number of bytes written.
*
* @throws asio::error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*
* @par Example:
* To write a single data buffer use the @ref buffer function as follows:
* @code
* socket.write_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers>
std::size_t write_some(const Const_Buffers& buffers)
{
return this->service.send(this->implementation, buffers, 0, throw_error());
}
/// Write some data to the socket.
/**
* This function is used to write data to the stream socket. The function call
* will block until one or more bytes of the data has been written
* successfully, or until an error occurs.
*
* @param buffers One or more data buffers to be written to the socket.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes written. Returns 0 if an error occurred and
* the error handler did not throw an exception.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*/
template <typename Const_Buffers, typename Error_Handler>
std::size_t write_some(const Const_Buffers& buffers,
Error_Handler error_handler)
{
return this->service.send(this->implementation, buffers, 0, error_handler);
}
/// Start an asynchronous write.
/**
* This function is used to asynchronously write data to the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more data buffers to be written to the socket.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the write operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The write operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example:
* To write a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_write_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Const_Buffers, typename Handler>
void async_write_some(const Const_Buffers& buffers, Handler handler)
{
this->service.async_send(this->implementation, buffers, 0, handler);
}
/// Read some data from the socket.
/**
* This function is used to read data from the stream socket. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws asio::error Thrown on failure. An error code of
* asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*
* @par Example:
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* socket.read_some(asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t read_some(const Mutable_Buffers& buffers)
{
return this->service.receive(this->implementation, buffers, 0,
throw_error());
}
/// Read some data from the socket.
/**
* This function is used to read data from the stream socket. The function
* call will block until one or more bytes of data has been read successfully,
* or until an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes read. Returns 0 if an error occurred and the
* error handler did not throw an exception.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*/
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t read_some(const Mutable_Buffers& buffers,
Error_Handler error_handler)
{
return this->service.receive(this->implementation, buffers, 0,
error_handler);
}
/// Start an asynchronous read.
/**
* This function is used to asynchronously read data from the stream socket.
* The function call always returns immediately.
*
* @param buffers One or more buffers into which the data will be read.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the handler is called.
*
* @param handler The handler to be called when the read operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code void handler(
* const asio::error& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @note The read operation may not read all of the requested number of bytes.
* Consider using the @ref async_read function if you need to ensure that the
* requested amount of data is read before the asynchronous operation
* completes.
*
* @par Example:
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* socket.async_read_some(asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers, typename Handler>
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
{
this->service.async_receive(this->implementation, buffers, 0, handler);
}
/// Peek at the incoming data on the stream socket.
/**
* This function is used to peek at the incoming data on the stream socket,
* without removing it from the input queue. The function call will block
* until data has been read successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws asio::error Thrown on failure.
*
* @par Example:
* To peek using a single data buffer use the @ref buffer function as
* follows:
* @code socket.peek(asio::buffer(data, size)); @endcode
* See the @ref buffer documentation for information on using multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename Mutable_Buffers>
std::size_t peek(const Mutable_Buffers& buffers)
{
return this->service.receive(this->implementation, buffers,
socket_base::message_peek, throw_error());
}
/// Peek at the incoming data on the stream socket.
/**
* This function is used to peek at the incoming data on the stream socket,
* without removing it from the input queue. The function call will block
* until data has been read successfully or an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation.
* ); @endcode
*
* @returns The number of bytes read. Returns 0 if an error occurred and the
* error handler did not throw an exception.
*/
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
{
return this->service.receive(this->implementation, buffers,
socket_base::message_peek, error_handler);
}
/// Determine the amount of data that may be read without blocking.
/**
* This function is used to determine the amount of data, in bytes, that may
* be read from the stream socket without blocking.
*
* @returns The number of bytes of data that can be read without blocking.
*
* @throws asio::error Thrown on failure.
*/
std::size_t in_avail()
{
socket_base::bytes_readable command;
this->service.io_control(this->implementation, command, throw_error());
return command.get();
}
/// Determine the amount of data that may be read without blocking.
/**
* This function is used to determine the amount of data, in bytes, that may
* be read from the stream socket without blocking.
*
* @param error_handler A handler to be called when the operation completes,
* to indicate whether or not an error has occurred. Copies will be made of
* the handler as required. The function signature of the handler must be:
* @code void error_handler(
* const asio::error& error // Result of operation
* ); @endcode
*
* @returns The number of bytes of data that can be read without blocking.
*/
template <typename Error_Handler>
std::size_t in_avail(Error_Handler error_handler)
{
socket_base::bytes_readable command;
this->service.io_control(this->implementation, command, error_handler);
return command.get();
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_STREAM_SOCKET_HPP

View File

@@ -0,0 +1,200 @@
//
// basic_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_STREAMBUF_HPP
#define ASIO_BASIC_STREAMBUF_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <algorithm>
#include <limits>
#include <memory>
#include <stdexcept>
#include <streambuf>
#include <vector>
#include "asio/detail/pop_options.hpp"
#include "asio/buffer.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
/// Automatically resizable buffer class based on std::streambuf.
template <typename Allocator = std::allocator<char> >
class basic_streambuf
: public std::streambuf,
private noncopyable
{
public:
#if defined(GENERATING_DOCUMENTATION)
/// The type used to represent the get area as a list of buffers.
typedef implementation_defined const_buffers_type;
/// The type used to represent the put area as a list of buffers.
typedef implementation_defined mutable_buffers_type;
#else
typedef asio::const_buffer_container_1 const_buffers_type;
typedef asio::mutable_buffer_container_1 mutable_buffers_type;
#endif
/// Construct a buffer with a specified maximum size.
explicit basic_streambuf(
std::size_t max_size = (std::numeric_limits<std::size_t>::max)(),
const Allocator& allocator = Allocator())
: max_size_(max_size),
buffer_(allocator)
{
std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta);
buffer_.resize((std::max<std::size_t>)(pend, 1));
setg(&buffer_[0], &buffer_[0], &buffer_[0]);
setp(&buffer_[0], &buffer_[0] + pend);
}
/// Return the size of the get area in characters.
std::size_t size() const
{
return pptr() - gptr();
}
/// Return the maximum size of the buffer.
std::size_t max_size() const
{
return max_size_;
}
/// Get a list of buffers that represents the get area.
const_buffers_type data() const
{
return asio::buffer(asio::const_buffer(gptr(),
(pptr() - gptr()) * sizeof(char_type)));
}
/// Get a list of buffers that represents the put area, with the given size.
mutable_buffers_type prepare(std::size_t size)
{
reserve(size);
return asio::buffer(asio::mutable_buffer(
pptr(), size * sizeof(char_type)));
}
/// Move the start of the put area by the specified number of characters.
void commit(std::size_t n)
{
if (pptr() + n > epptr())
n = epptr() - pptr();
pbump(static_cast<int>(n));
}
/// Move the start of the get area by the specified number of characters.
void consume(std::size_t n)
{
while (n > 0)
{
sbumpc();
--n;
}
}
protected:
enum { buffer_delta = 128 };
int_type underflow()
{
if (gptr() < pptr())
{
setg(&buffer_[0], gptr(), pptr());
return traits_type::to_int_type(*gptr());
}
else
{
return traits_type::eof();
}
}
int_type overflow(int_type c)
{
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
if (pptr() == epptr())
{
std::size_t buffer_size = pptr() - gptr();
if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta)
{
reserve(max_size_ - buffer_size);
}
else
{
reserve(buffer_delta);
}
}
*pptr() = traits_type::to_char_type(c);
pbump(1);
return c;
}
return traits_type::not_eof(c);
}
void reserve(std::size_t n)
{
// Get current stream positions as offsets.
std::size_t gnext = gptr() - &buffer_[0];
std::size_t gend = egptr() - &buffer_[0];
std::size_t pnext = pptr() - &buffer_[0];
std::size_t pend = epptr() - &buffer_[0];
// Check if there is already enough space in the put area.
if (n <= pend - pnext)
{
return;
}
// Shift existing contents of get area to start of buffer.
if (gnext > 0)
{
std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend);
gend -= gnext;
pnext -= gnext;
}
// Ensure buffer is large enough to hold at least the specified size.
if (n > pend - pnext)
{
if (n <= max_size_ && pnext <= max_size_ - n)
{
buffer_.resize((std::max<std::size_t>)(pnext + n, 1));
}
else
{
throw std::length_error("asio::streambuf too long");
}
}
// Update stream positions.
setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend);
setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n);
}
private:
std::size_t max_size_;
std::vector<char_type, Allocator> buffer_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_STREAMBUF_HPP

View File

@@ -0,0 +1,639 @@
//
// buffer.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFER_HPP
#define ASIO_BUFFER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include <boost/array.hpp>
#include <boost/type_traits/is_const.hpp>
#include <string>
#include <vector>
#include "asio/detail/pop_options.hpp"
namespace asio {
class mutable_buffer;
class const_buffer;
namespace detail {
void* buffer_cast_helper(const mutable_buffer&);
const void* buffer_cast_helper(const const_buffer&);
std::size_t buffer_size_helper(const mutable_buffer&);
std::size_t buffer_size_helper(const const_buffer&);
} // namespace detail
/// Holds a buffer that can be modified.
/**
* The mutable_buffer class provides a safe representation of a buffer that can
* be modified. It does not own the underlying data, and so is cheap to copy or
* assign.
*/
class mutable_buffer
{
public:
/// Construct an empty buffer.
mutable_buffer()
: data_(0),
size_(0)
{
}
/// Construct a buffer to represent a given memory range.
mutable_buffer(void* data, std::size_t size)
: data_(data),
size_(size)
{
}
private:
friend void* asio::detail::buffer_cast_helper(
const mutable_buffer& b);
friend std::size_t asio::detail::buffer_size_helper(
const mutable_buffer& b);
void* data_;
std::size_t size_;
};
namespace detail {
inline void* buffer_cast_helper(const mutable_buffer& b)
{
return b.data_;
}
inline std::size_t buffer_size_helper(const mutable_buffer& b)
{
return b.size_;
}
} // namespace detail
/// Cast a non-modifiable buffer to a specified pointer to POD type.
/**
* @relates mutable_buffer
*/
template <typename Pointer_To_Pod_Type>
inline Pointer_To_Pod_Type buffer_cast(const mutable_buffer& b)
{
return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
}
/// Get the number of bytes in a non-modifiable buffer.
/**
* @relates mutable_buffer
*/
inline std::size_t buffer_size(const mutable_buffer& b)
{
return detail::buffer_size_helper(b);
}
/// Create a new modifiable buffer that is offset from the start of another.
/**
* @relates mutable_buffer
*/
inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start)
{
if (start > buffer_size(b))
return mutable_buffer();
char* new_data = buffer_cast<char*>(b) + start;
std::size_t new_size = buffer_size(b) - start;
return mutable_buffer(new_data, new_size);
}
/// Create a new modifiable buffer that is offset from the start of another.
/**
* @relates mutable_buffer
*/
inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b)
{
if (start > buffer_size(b))
return mutable_buffer();
char* new_data = buffer_cast<char*>(b) + start;
std::size_t new_size = buffer_size(b) - start;
return mutable_buffer(new_data, new_size);
}
/// Adapts a single modifiable buffer so that it meets the requirements of the
/// Mutable_Buffers concept.
class mutable_buffer_container_1
: public mutable_buffer
{
public:
/// The type for each element in the list of buffers.
typedef mutable_buffer value_type;
/// A random-access iterator type that may be used to read elements.
typedef const mutable_buffer* const_iterator;
/// Construct to represent a single modifiable buffer.
explicit mutable_buffer_container_1(const mutable_buffer& b)
: mutable_buffer(b)
{
}
/// Get a random-access iterator to the first element.
const_iterator begin() const
{
return this;
}
/// Get a random-access iterator for one past the last element.
const_iterator end() const
{
return begin() + 1;
}
};
/// Holds a buffer that cannot be modified.
/**
* The const_buffer class provides a safe representation of a buffer that cannot
* be modified. It does not own the underlying data, and so is cheap to copy or
* assign.
*/
class const_buffer
{
public:
/// Construct an empty buffer.
const_buffer()
: data_(0),
size_(0)
{
}
/// Construct a buffer to represent a given memory range.
const_buffer(const void* data, std::size_t size)
: data_(data),
size_(size)
{
}
/// Construct a non-modifiable buffer from a modifiable one.
const_buffer(const mutable_buffer& b)
: data_(asio::detail::buffer_cast_helper(b)),
size_(asio::detail::buffer_size_helper(b))
{
}
private:
friend const void* asio::detail::buffer_cast_helper(
const const_buffer& b);
friend std::size_t asio::detail::buffer_size_helper(
const const_buffer& b);
const void* data_;
std::size_t size_;
};
namespace detail {
inline const void* buffer_cast_helper(const const_buffer& b)
{
return b.data_;
}
inline std::size_t buffer_size_helper(const const_buffer& b)
{
return b.size_;
}
} // namespace detail
/// Cast a non-modifiable buffer to a specified pointer to POD type.
/**
* @relates const_buffer
*/
template <typename Pointer_To_Pod_Type>
inline Pointer_To_Pod_Type buffer_cast(const const_buffer& b)
{
return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
}
/// Get the number of bytes in a non-modifiable buffer.
/**
* @relates const_buffer
*/
inline std::size_t buffer_size(const const_buffer& b)
{
return detail::buffer_size_helper(b);
}
/// Create a new non-modifiable buffer that is offset from the start of another.
/**
* @relates const_buffer
*/
inline const_buffer operator+(const const_buffer& b, std::size_t start)
{
if (start > buffer_size(b))
return const_buffer();
const char* new_data = buffer_cast<const char*>(b) + start;
std::size_t new_size = buffer_size(b) - start;
return const_buffer(new_data, new_size);
}
/// Create a new non-modifiable buffer that is offset from the start of another.
/**
* @relates const_buffer
*/
inline const_buffer operator+(std::size_t start, const const_buffer& b)
{
if (start > buffer_size(b))
return const_buffer();
const char* new_data = buffer_cast<const char*>(b) + start;
std::size_t new_size = buffer_size(b) - start;
return const_buffer(new_data, new_size);
}
/// Adapts a single non-modifiable buffer so that it meets the requirements of
/// the Const_Buffers concept.
class const_buffer_container_1
: public const_buffer
{
public:
/// The type for each element in the list of buffers.
typedef const_buffer value_type;
/// A random-access iterator type that may be used to read elements.
typedef const const_buffer* const_iterator;
/// Construct to represent a single non-modifiable buffer.
explicit const_buffer_container_1(const const_buffer& b)
: const_buffer(b)
{
}
/// Get a random-access iterator to the first element.
const_iterator begin() const
{
return this;
}
/// Get a random-access iterator for one past the last element.
const_iterator end() const
{
return begin() + 1;
}
};
/** @defgroup buffer asio::buffer
*
* @brief The asio::buffer function is used to create a buffer object to
* represent raw memory, an array of POD elements, or a vector of POD elements.
*
* The simplest use case involves reading or writing a single buffer of a
* specified size:
*
* @code sock.write(asio::buffer(data, size)); @endcode
*
* In the above example, the return value of asio::buffer meets the
* requirements of the Const_Buffers concept so that it may be directly passed
* to the socket's write function. A buffer created for modifiable memory also
* meets the requirements of the Mutable_Buffers concept.
*
* An individual buffer may be created from a builtin array, std::vector or
* boost::array of POD elements. This helps prevent buffer overruns by
* automatically determining the size of the buffer:
*
* @code char d1[128];
* size_t bytes_transferred = sock.read(asio::buffer(d1));
*
* std::vector<char> d2(128);
* bytes_transferred = sock.read(asio::buffer(d2));
*
* boost::array<char, 128> d3;
* bytes_transferred = sock.read(asio::buffer(d3)); @endcode
*
* To read or write using multiple buffers (i.e. scatter-gather I/O), multiple
* buffer objects may be assigned into a container that supports the
* Mutable_Buffers (for read) or Const_Buffers (for write) concepts:
*
* @code
* char d1[128];
* std::vector<char> d2(128);
* boost::array<char, 128> d3;
*
* boost::array<mutable_buffer, 3> bufs1 = {
* asio::buffer(d1),
* asio::buffer(d2),
* asio::buffer(d3) };
* bytes_transferred = sock.read(bufs1);
*
* std::vector<const_buffer> bufs2;
* bufs2.push_back(asio::buffer(d1));
* bufs2.push_back(asio::buffer(d2));
* bufs2.push_back(asio::buffer(d3));
* bytes_transferred = sock.write(bufs2); @endcode
*/
/*@{*/
/// Create a new modifiable buffer from an existing buffer.
inline mutable_buffer_container_1 buffer(const mutable_buffer& b)
{
return mutable_buffer_container_1(b);
}
/// Create a new modifiable buffer from an existing buffer.
inline mutable_buffer_container_1 buffer(const mutable_buffer& b,
std::size_t max_size_in_bytes)
{
return mutable_buffer_container_1(
mutable_buffer(buffer_cast<void*>(b),
buffer_size(b) < max_size_in_bytes
? buffer_size(b) : max_size_in_bytes));
}
/// Create a new non-modifiable buffer from an existing buffer.
inline const_buffer_container_1 buffer(const const_buffer& b)
{
return const_buffer_container_1(b);
}
/// Create a new non-modifiable buffer from an existing buffer.
inline const_buffer_container_1 buffer(const const_buffer& b,
std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(buffer_cast<const void*>(b),
buffer_size(b) < max_size_in_bytes
? buffer_size(b) : max_size_in_bytes));
}
/// Create a new modifiable buffer that represents the given memory range.
inline mutable_buffer_container_1 buffer(void* data, std::size_t size_in_bytes)
{
return mutable_buffer_container_1(mutable_buffer(data, size_in_bytes));
}
/// Create a new non-modifiable buffer that represents the given memory range.
inline const_buffer_container_1 buffer(const void* data,
std::size_t size_in_bytes)
{
return const_buffer_container_1(const_buffer(data, size_in_bytes));
}
/// Create a new modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N])
{
return mutable_buffer_container_1(mutable_buffer(data, N * sizeof(Pod_Type)));
}
/// Create a new modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N],
std::size_t max_size_in_bytes)
{
return mutable_buffer_container_1(
mutable_buffer(data,
N * sizeof(Pod_Type) < max_size_in_bytes
? N * sizeof(Pod_Type) : max_size_in_bytes));
}
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(const Pod_Type (&data)[N])
{
return const_buffer_container_1(const_buffer(data, N * sizeof(Pod_Type)));
}
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(const Pod_Type (&data)[N],
std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(data,
N * sizeof(Pod_Type) < max_size_in_bytes
? N * sizeof(Pod_Type) : max_size_in_bytes));
}
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
// Borland C++ thinks the overloads:
//
// unspecified buffer(boost::array<Pod_Type, N>& array ...);
//
// and
//
// unspecified buffer(boost::array<const Pod_Type, N>& array ...);
//
// are ambiguous. This will be worked around by using a buffer_types traits
// class that contains typedefs for the appropriate buffer and container
// classes, based on whether Pod_Type is const or non-const.
namespace detail {
template <bool IsConst>
struct buffer_types_base;
template <>
struct buffer_types_base<false>
{
typedef mutable_buffer buffer_type;
typedef mutable_buffer_container_1 container_type;
};
template <>
struct buffer_types_base<true>
{
typedef const_buffer buffer_type;
typedef const_buffer_container_1 container_type;
};
template <typename Pod_Type>
struct buffer_types
: public buffer_types_base<boost::is_const<Pod_Type>::value>
{
};
} // namespace detail
template <typename Pod_Type, std::size_t N>
inline typename detail::buffer_types<Pod_Type>::container_type
buffer(boost::array<Pod_Type, N>& data)
{
typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
buffer_type;
typedef typename asio::detail::buffer_types<Pod_Type>::container_type
container_type;
return container_type(
buffer_type(data.c_array(), data.size() * sizeof(Pod_Type)));
}
template <typename Pod_Type, std::size_t N>
inline typename detail::buffer_types<Pod_Type>::container_type
buffer(boost::array<Pod_Type, N>& data, std::size_t max_size_in_bytes)
{
typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
buffer_type;
typedef typename asio::detail::buffer_types<Pod_Type>::container_type
container_type;
return container_type(
buffer_type(data.c_array(),
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
/// Create a new modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data)
{
return mutable_buffer_container_1(
mutable_buffer(data.c_array(), data.size() * sizeof(Pod_Type)));
}
/// Create a new modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data,
std::size_t max_size_in_bytes)
{
return mutable_buffer_container_1(
mutable_buffer(data.c_array(),
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data)
{
return const_buffer_container_1(
const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
}
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data,
std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(data.data(),
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data)
{
return const_buffer_container_1(
const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
}
/// Create a new non-modifiable buffer that represents the given POD array.
template <typename Pod_Type, std::size_t N>
inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data,
std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(data.data(),
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
/// Create a new modifiable buffer that represents the given POD vector.
/**
* @note The buffer is invalidated by any vector operation that would also
* invalidate iterators.
*/
template <typename Pod_Type, typename Allocator>
inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data)
{
return mutable_buffer_container_1(
mutable_buffer(&data[0], data.size() * sizeof(Pod_Type)));
}
/// Create a new modifiable buffer that represents the given POD vector.
/**
* @note The buffer is invalidated by any vector operation that would also
* invalidate iterators.
*/
template <typename Pod_Type, typename Allocator>
inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data,
std::size_t max_size_in_bytes)
{
return mutable_buffer_container_1(
mutable_buffer(&data[0],
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
/// Create a new non-modifiable buffer that represents the given POD vector.
/**
* @note The buffer is invalidated by any vector operation that would also
* invalidate iterators.
*/
template <typename Pod_Type, typename Allocator>
inline const_buffer_container_1 buffer(
const std::vector<Pod_Type, Allocator>& data)
{
return const_buffer_container_1(
const_buffer(&data[0], data.size() * sizeof(Pod_Type)));
}
/// Create a new non-modifiable buffer that represents the given POD vector.
/**
* @note The buffer is invalidated by any vector operation that would also
* invalidate iterators.
*/
template <typename Pod_Type, typename Allocator>
inline const_buffer_container_1 buffer(
const std::vector<Pod_Type, Allocator>& data, std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(&data[0],
data.size() * sizeof(Pod_Type) < max_size_in_bytes
? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
}
/// Create a new non-modifiable buffer that represents the given string.
/**
* @note The buffer is invalidated by any non-const operation called on the
* given string object.
*/
inline const_buffer_container_1 buffer(const std::string& data)
{
return const_buffer_container_1(const_buffer(data.data(), data.size()));
}
/// Create a new non-modifiable buffer that represents the given string.
/**
* @note The buffer is invalidated by any non-const operation called on the
* given string object.
*/
inline const_buffer_container_1 buffer(const std::string& data,
std::size_t max_size_in_bytes)
{
return const_buffer_container_1(
const_buffer(data.data(),
data.size() < max_size_in_bytes
? data.size() : max_size_in_bytes));
}
/*@}*/
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFER_HPP

View File

@@ -0,0 +1,407 @@
//
// buffered_read_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_READ_STREAM_HPP
#define ASIO_BUFFERED_READ_STREAM_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <cstring>
#include <boost/config.hpp>
#include <boost/type_traits.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_resize_guard.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
/// Adds buffering to the read-related operations of a stream.
/**
* The buffered_read_stream class template can be used to add buffering to the
* synchronous and asynchronous read operations of a stream.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
* Sync_Read_Stream, Sync_Write_Stream.
*/
template <typename Stream>
class buffered_read_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename boost::remove_reference<Stream>::type next_layer_type;
/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
/// The type used for reporting errors.
typedef typename next_layer_type::error_type error_type;
#if defined(GENERATING_DOCUMENTATION)
/// The default buffer size.
static const std::size_t default_buffer_size = implementation_defined;
#else
BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
#endif
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_read_stream(Arg& a)
: next_layer_(a),
storage_(default_buffer_size)
{
}
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
buffered_read_stream(Arg& a, std::size_t buffer_size)
: next_layer_(a),
storage_(buffer_size)
{
}
/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return next_layer_;
}
/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return next_layer_.lowest_layer();
}
/// Get the io_service associated with the object.
asio::io_service& io_service()
{
return next_layer_.io_service();
}
/// Close the stream.
void close()
{
next_layer_.close();
}
/// Close the stream.
template <typename Error_Handler>
void close(Error_Handler error_handler)
{
next_layer_.close(error_handler);
}
/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename Const_Buffers>
std::size_t write_some(const Const_Buffers& buffers)
{
return next_layer_.write_some(buffers);
}
/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Const_Buffers, typename Error_Handler>
std::size_t write_some(const Const_Buffers& buffers,
Error_Handler error_handler)
{
return next_layer_.write_some(buffers, error_handler);
}
/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename Const_Buffers, typename Handler>
void async_write_some(const Const_Buffers& buffers, Handler handler)
{
next_layer_.async_write_some(buffers, handler);
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation. Throws an exception on failure.
std::size_t fill()
{
detail::buffer_resize_guard<detail::buffered_stream_storage>
resize_guard(storage_);
std::size_t previous_size = storage_.size();
storage_.resize(storage_.capacity());
storage_.resize(previous_size + next_layer_.read_some(buffer(
storage_.data() + previous_size,
storage_.size() - previous_size)));
resize_guard.commit();
return storage_.size() - previous_size;
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if an error occurred and the
/// error handler did not throw.
template <typename Error_Handler>
std::size_t fill(Error_Handler error_handler)
{
detail::buffer_resize_guard<detail::buffered_stream_storage>
resize_guard(storage_);
std::size_t previous_size = storage_.size();
storage_.resize(storage_.capacity());
storage_.resize(previous_size + next_layer_.read_some(buffer(
storage_.data() + previous_size,
storage_.size() - previous_size),
error_handler));
resize_guard.commit();
return storage_.size() - previous_size;
}
template <typename Handler>
class fill_handler
{
public:
fill_handler(asio::io_service& io_service,
detail::buffered_stream_storage& storage,
std::size_t previous_size, Handler handler)
: io_service_(io_service),
storage_(storage),
previous_size_(previous_size),
handler_(handler)
{
}
template <typename Error>
void operator()(const Error& e, std::size_t bytes_transferred)
{
storage_.resize(previous_size_ + bytes_transferred);
io_service_.dispatch(detail::bind_handler(
handler_, e, bytes_transferred));
}
private:
asio::io_service& io_service_;
detail::buffered_stream_storage& storage_;
std::size_t previous_size_;
Handler handler_;
};
/// Start an asynchronous fill.
template <typename Handler>
void async_fill(Handler handler)
{
std::size_t previous_size = storage_.size();
storage_.resize(storage_.capacity());
next_layer_.async_read_some(
buffer(
storage_.data() + previous_size,
storage_.size() - previous_size),
fill_handler<Handler>(io_service(), storage_, previous_size, handler));
}
/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename Mutable_Buffers>
std::size_t read_some(const Mutable_Buffers& buffers)
{
if (storage_.empty())
fill();
return copy(buffers);
}
/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred and the error handler did not throw an exception.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t read_some(const Mutable_Buffers& buffers,
Error_Handler error_handler)
{
if (storage_.empty() && !fill(error_handler))
return 0;
return copy(buffers);
}
template <typename Mutable_Buffers, typename Handler>
class read_some_handler
{
public:
read_some_handler(asio::io_service& io_service,
detail::buffered_stream_storage& storage,
const Mutable_Buffers& buffers, Handler handler)
: io_service_(io_service),
storage_(storage),
buffers_(buffers),
handler_(handler)
{
}
void operator()(const error_type& e, std::size_t)
{
if (e || storage_.empty())
{
std::size_t length = 0;
io_service_.dispatch(detail::bind_handler(handler_, e, length));
}
else
{
using namespace std; // For memcpy.
std::size_t bytes_avail = storage_.size();
std::size_t bytes_copied = 0;
typename Mutable_Buffers::const_iterator iter = buffers_.begin();
typename Mutable_Buffers::const_iterator end = buffers_.end();
for (; iter != end && bytes_avail > 0; ++iter)
{
std::size_t max_length = buffer_size(*iter);
std::size_t length = (max_length < bytes_avail)
? max_length : bytes_avail;
memcpy(buffer_cast<void*>(*iter),
storage_.data() + bytes_copied, length);
bytes_copied += length;
bytes_avail -= length;
}
storage_.consume(bytes_copied);
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
}
}
private:
asio::io_service& io_service_;
detail::buffered_stream_storage& storage_;
Mutable_Buffers buffers_;
Handler handler_;
};
/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename Mutable_Buffers, typename Handler>
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
{
if (storage_.empty())
{
async_fill(read_some_handler<Mutable_Buffers, Handler>(
io_service(), storage_, buffers, handler));
}
else
{
std::size_t length = copy(buffers);
io_service().post(detail::bind_handler(handler, 0, length));
}
}
/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename Mutable_Buffers>
std::size_t peek(const Mutable_Buffers& buffers)
{
if (storage_.empty())
fill();
return peek_copy(buffers);
}
/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
{
if (storage_.empty() && !fill(error_handler))
return 0;
return peek_copy(buffers);
}
/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return storage_.size();
}
/// Determine the amount of data that may be read without blocking.
template <typename Error_Handler>
std::size_t in_avail(Error_Handler error_handler)
{
return storage_.size();
}
private:
/// Copy data out of the internal buffer to the specified target buffer.
/// Returns the number of bytes copied.
template <typename Mutable_Buffers>
std::size_t copy(const Mutable_Buffers& buffers)
{
using namespace std; // For memcpy.
std::size_t bytes_avail = storage_.size();
std::size_t bytes_copied = 0;
typename Mutable_Buffers::const_iterator iter = buffers.begin();
typename Mutable_Buffers::const_iterator end = buffers.end();
for (; iter != end && bytes_avail > 0; ++iter)
{
std::size_t max_length = buffer_size(*iter);
std::size_t length = (max_length < bytes_avail)
? max_length : bytes_avail;
memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
bytes_copied += length;
bytes_avail -= length;
}
storage_.consume(bytes_copied);
return bytes_copied;
}
/// Copy data from the internal buffer to the specified target buffer, without
/// removing the data from the internal buffer. Returns the number of bytes
/// copied.
template <typename Mutable_Buffers>
std::size_t peek_copy(const Mutable_Buffers& buffers)
{
using namespace std; // For memcpy.
std::size_t bytes_avail = storage_.size();
std::size_t bytes_copied = 0;
typename Mutable_Buffers::const_iterator iter = buffers.begin();
typename Mutable_Buffers::const_iterator end = buffers.end();
for (; iter != end && bytes_avail > 0; ++iter)
{
std::size_t max_length = buffer_size(*iter);
std::size_t length = (max_length < bytes_avail)
? max_length : bytes_avail;
memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
bytes_copied += length;
bytes_avail -= length;
}
return bytes_copied;
}
/// The next layer.
Stream next_layer_;
// The data in the buffer.
detail::buffered_stream_storage storage_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_READ_STREAM_HPP

View File

@@ -0,0 +1,29 @@
//
// buffered_read_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP
#define ASIO_BUFFERED_READ_STREAM_FWD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename Stream>
class buffered_read_stream;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP

View File

@@ -0,0 +1,249 @@
//
// buffered_stream.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_STREAM_HPP
#define ASIO_BUFFERED_STREAM_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
/// Adds buffering to the read- and write-related operations of a stream.
/**
* The buffered_stream class template can be used to add buffering to the
* synchronous and asynchronous read and write operations of a stream.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
* Sync_Read_Stream, Sync_Write_Stream.
*/
template <typename Stream>
class buffered_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename boost::remove_reference<Stream>::type next_layer_type;
/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
/// The type used for reporting errors.
typedef typename next_layer_type::error_type error_type;
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_stream(Arg& a)
: inner_stream_impl_(a),
stream_impl_(inner_stream_impl_)
{
}
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_stream(Arg& a, std::size_t read_buffer_size,
std::size_t write_buffer_size)
: inner_stream_impl_(a, write_buffer_size),
stream_impl_(inner_stream_impl_, read_buffer_size)
{
}
/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return stream_impl_.next_layer().next_layer();
}
/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return stream_impl_.lowest_layer();
}
/// Get the io_service associated with the object.
asio::io_service& io_service()
{
return stream_impl_.io_service();
}
/// Close the stream.
void close()
{
stream_impl_.close();
}
/// Close the stream.
template <typename Error_Handler>
void close(Error_Handler error_handler)
{
stream_impl_.close(error_handler);
}
/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation. Throws an
/// exception on failure.
std::size_t flush()
{
return stream_impl_.next_layer().flush();
}
/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation, or 0 if an
/// error occurred and the error handler did not throw.
template <typename Error_Handler>
std::size_t flush(Error_Handler error_handler)
{
return stream_impl_.next_layer().flush(error_handler);
}
/// Start an asynchronous flush.
template <typename Handler>
void async_flush(Handler handler)
{
return stream_impl_.next_layer().async_flush(handler);
}
/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename Const_Buffers>
std::size_t write_some(const Const_Buffers& buffers)
{
return stream_impl_.write_some(buffers);
}
/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Const_Buffers, typename Error_Handler>
std::size_t write_some(const Const_Buffers& buffers,
Error_Handler error_handler)
{
return stream_impl_.write_some(buffers, error_handler);
}
/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename Const_Buffers, typename Handler>
void async_write_some(const Const_Buffers& buffers, Handler handler)
{
stream_impl_.async_write_some(buffers, handler);
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation. Throws an exception on failure.
std::size_t fill()
{
return stream_impl_.fill();
}
/// Fill the buffer with some data. Returns the number of bytes placed in the
/// buffer as a result of the operation, or 0 if an error occurred and the
/// error handler did not throw.
template <typename Error_Handler>
std::size_t fill(Error_Handler error_handler)
{
return stream_impl_.fill(error_handler);
}
/// Start an asynchronous fill.
template <typename Handler>
void async_fill(Handler handler)
{
stream_impl_.async_fill(handler);
}
/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename Mutable_Buffers>
std::size_t read_some(const Mutable_Buffers& buffers)
{
return stream_impl_.read_some(buffers);
}
/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred and the error handler did not throw an exception.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t read_some(const Mutable_Buffers& buffers,
Error_Handler error_handler)
{
return stream_impl_.read_some(buffers, error_handler);
}
/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename Mutable_Buffers, typename Handler>
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
{
stream_impl_.async_read_some(buffers, handler);
}
/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename Mutable_Buffers>
std::size_t peek(const Mutable_Buffers& buffers)
{
return stream_impl_.peek(buffers);
}
/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
{
return stream_impl_.peek(buffers, error_handler);
}
/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return stream_impl_.in_avail();
}
/// Determine the amount of data that may be read without blocking.
template <typename Error_Handler>
std::size_t in_avail(Error_Handler error_handler)
{
return stream_impl_.in_avail(error_handler);
}
private:
// The buffered write stream.
typedef buffered_write_stream<Stream> write_stream_type;
write_stream_type inner_stream_impl_;
// The buffered read stream.
typedef buffered_read_stream<write_stream_type&> read_stream_type;
read_stream_type stream_impl_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_STREAM_HPP

View File

@@ -0,0 +1,29 @@
//
// buffered_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_STREAM_FWD_HPP
#define ASIO_BUFFERED_STREAM_FWD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename Stream>
class buffered_stream;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_STREAM_FWD_HPP

View File

@@ -0,0 +1,362 @@
//
// buffered_write_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP
#define ASIO_BUFFERED_WRITE_STREAM_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <cstring>
#include <boost/config.hpp>
#include <boost/type_traits.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/completion_condition.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/write.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
/// Adds buffering to the write-related operations of a stream.
/**
* The buffered_write_stream class template can be used to add buffering to the
* synchronous and asynchronous write operations of a stream.
*
* @par Thread Safety:
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Concepts:
* Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
* Sync_Read_Stream, Sync_Write_Stream.
*/
template <typename Stream>
class buffered_write_stream
: private noncopyable
{
public:
/// The type of the next layer.
typedef typename boost::remove_reference<Stream>::type next_layer_type;
/// The type of the lowest layer.
typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
/// The type used for reporting errors.
typedef typename next_layer_type::error_type error_type;
#if defined(GENERATING_DOCUMENTATION)
/// The default buffer size.
static const std::size_t default_buffer_size = implementation_defined;
#else
BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
#endif
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
explicit buffered_write_stream(Arg& a)
: next_layer_(a),
storage_(default_buffer_size)
{
}
/// Construct, passing the specified argument to initialise the next layer.
template <typename Arg>
buffered_write_stream(Arg& a, std::size_t buffer_size)
: next_layer_(a),
storage_(buffer_size)
{
}
/// Get a reference to the next layer.
next_layer_type& next_layer()
{
return next_layer_;
}
/// Get a reference to the lowest layer.
lowest_layer_type& lowest_layer()
{
return next_layer_.lowest_layer();
}
/// Get the io_service associated with the object.
asio::io_service& io_service()
{
return next_layer_.io_service();
}
/// Close the stream.
void close()
{
next_layer_.close();
}
/// Close the stream.
template <typename Error_Handler>
void close(Error_Handler error_handler)
{
next_layer_.close(error_handler);
}
/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation. Throws an
/// exception on failure.
std::size_t flush()
{
std::size_t bytes_written = write(next_layer_,
buffer(storage_.data(), storage_.size()));
storage_.consume(bytes_written);
return bytes_written;
}
/// Flush all data from the buffer to the next layer. Returns the number of
/// bytes written to the next layer on the last write operation, or 0 if an
/// error occurred and the error handler did not throw.
template <typename Error_Handler>
std::size_t flush(Error_Handler error_handler)
{
std::size_t bytes_written = write(next_layer_,
buffer(storage_.data(), storage_.size()),
transfer_all(), error_handler);
storage_.consume(bytes_written);
return bytes_written;
}
template <typename Handler>
class flush_handler
{
public:
flush_handler(asio::io_service& io_service,
detail::buffered_stream_storage& storage, Handler handler)
: io_service_(io_service),
storage_(storage),
handler_(handler)
{
}
void operator()(const error_type& e, std::size_t bytes_written)
{
storage_.consume(bytes_written);
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_written));
}
private:
asio::io_service& io_service_;
detail::buffered_stream_storage& storage_;
Handler handler_;
};
/// Start an asynchronous flush.
template <typename Handler>
void async_flush(Handler handler)
{
async_write(next_layer_, buffer(storage_.data(), storage_.size()),
flush_handler<Handler>(io_service(), storage_, handler));
}
/// Write the given data to the stream. Returns the number of bytes written.
/// Throws an exception on failure.
template <typename Const_Buffers>
std::size_t write_some(const Const_Buffers& buffers)
{
if (storage_.size() == storage_.capacity())
flush();
return copy(buffers);
}
/// Write the given data to the stream. Returns the number of bytes written,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Const_Buffers, typename Error_Handler>
std::size_t write_some(const Const_Buffers& buffers,
Error_Handler error_handler)
{
if (storage_.size() == storage_.capacity() && !flush(error_handler))
return 0;
return copy(buffers);
}
template <typename Const_Buffers, typename Handler>
class write_some_handler
{
public:
write_some_handler(asio::io_service& io_service,
detail::buffered_stream_storage& storage,
const Const_Buffers& buffers, Handler handler)
: io_service_(io_service),
storage_(storage),
buffers_(buffers),
handler_(handler)
{
}
void operator()(const error_type& e, std::size_t)
{
if (e)
{
std::size_t length = 0;
io_service_.dispatch(detail::bind_handler(handler_, e, length));
}
else
{
using namespace std; // For memcpy.
std::size_t orig_size = storage_.size();
std::size_t space_avail = storage_.capacity() - orig_size;
std::size_t bytes_copied = 0;
typename Const_Buffers::const_iterator iter = buffers_.begin();
typename Const_Buffers::const_iterator end = buffers_.end();
for (; iter != end && space_avail > 0; ++iter)
{
std::size_t bytes_avail = buffer_size(*iter);
std::size_t length = (bytes_avail < space_avail)
? bytes_avail : space_avail;
storage_.resize(orig_size + bytes_copied + length);
memcpy(storage_.data() + orig_size + bytes_copied,
buffer_cast<const void*>(*iter), length);
bytes_copied += length;
space_avail -= length;
}
io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
}
}
private:
asio::io_service& io_service_;
detail::buffered_stream_storage& storage_;
Const_Buffers buffers_;
Handler handler_;
};
/// Start an asynchronous write. The data being written must be valid for the
/// lifetime of the asynchronous operation.
template <typename Const_Buffers, typename Handler>
void async_write_some(const Const_Buffers& buffers, Handler handler)
{
if (storage_.size() == storage_.capacity())
{
async_flush(write_some_handler<Const_Buffers, Handler>(
io_service(), storage_, buffers, handler));
}
else
{
std::size_t bytes_copied = copy(buffers);
io_service().post(detail::bind_handler(handler, 0, bytes_copied));
}
}
/// Read some data from the stream. Returns the number of bytes read. Throws
/// an exception on failure.
template <typename Mutable_Buffers>
std::size_t read_some(const Mutable_Buffers& buffers)
{
return next_layer_.read_some(buffers);
}
/// Read some data from the stream. Returns the number of bytes read or 0 if
/// an error occurred and the error handler did not throw an exception.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t read_some(const Mutable_Buffers& buffers,
Error_Handler error_handler)
{
return next_layer_.read_some(buffers, error_handler);
}
/// Start an asynchronous read. The buffer into which the data will be read
/// must be valid for the lifetime of the asynchronous operation.
template <typename Mutable_Buffers, typename Handler>
void async_read_some(const Mutable_Buffers& buffers, Handler handler)
{
next_layer_.async_read_some(buffers, handler);
}
/// Peek at the incoming data on the stream. Returns the number of bytes read.
/// Throws an exception on failure.
template <typename Mutable_Buffers>
std::size_t peek(const Mutable_Buffers& buffers)
{
return next_layer_.peek(buffers);
}
/// Peek at the incoming data on the stream. Returns the number of bytes read,
/// or 0 if an error occurred and the error handler did not throw.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
{
return next_layer_.peek(buffers, error_handler);
}
/// Determine the amount of data that may be read without blocking.
std::size_t in_avail()
{
return next_layer_.in_avail();
}
/// Determine the amount of data that may be read without blocking.
template <typename Error_Handler>
std::size_t in_avail(Error_Handler error_handler)
{
return next_layer_.in_avail(error_handler);
}
private:
/// Copy data into the internal buffer from the specified source buffer.
/// Returns the number of bytes copied.
template <typename Const_Buffers>
std::size_t copy(const Const_Buffers& buffers)
{
using namespace std; // For memcpy.
std::size_t orig_size = storage_.size();
std::size_t space_avail = storage_.capacity() - orig_size;
std::size_t bytes_copied = 0;
typename Const_Buffers::const_iterator iter = buffers.begin();
typename Const_Buffers::const_iterator end = buffers.end();
for (; iter != end && space_avail > 0; ++iter)
{
std::size_t bytes_avail = buffer_size(*iter);
std::size_t length = (bytes_avail < space_avail)
? bytes_avail : space_avail;
storage_.resize(orig_size + bytes_copied + length);
memcpy(storage_.data() + orig_size + bytes_copied,
buffer_cast<const void*>(*iter), length);
bytes_copied += length;
space_avail -= length;
}
return bytes_copied;
}
/// The next layer.
Stream next_layer_;
// The data in the buffer.
detail::buffered_stream_storage storage_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_WRITE_STREAM_HPP

View File

@@ -0,0 +1,29 @@
//
// buffered_write_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename Stream>
class buffered_write_stream;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP

View File

@@ -0,0 +1,101 @@
//
// completion_condition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_COMPLETION_CONDITION_HPP
#define ASIO_COMPLETION_CONDITION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
class transfer_all_t
{
public:
typedef bool result_type;
template <typename Error>
bool operator()(const Error& err, std::size_t)
{
return !!err;
}
};
class transfer_at_least_t
{
public:
typedef bool result_type;
explicit transfer_at_least_t(std::size_t minimum)
: minimum_(minimum)
{
}
template <typename Error>
bool operator()(const Error& err, std::size_t bytes_transferred)
{
return !!err || bytes_transferred >= minimum_;
}
private:
std::size_t minimum_;
};
} // namespace detail
/**
* @defgroup completion_condition Completion Condition Function Objects
*
* Function objects used for determining when a read or write operation should
* complete.
*/
/*@{*/
/// Return a completion condition function object that indicates that a read or
/// write operation should continue until all of the data has been transferred,
/// or until an error occurs.
#if defined(GENERATING_DOCUMENTATION)
unspecified transfer_all();
#else
inline detail::transfer_all_t transfer_all()
{
return detail::transfer_all_t();
}
#endif
/// Return a completion condition function object that indicates that a read or
/// write operation should continue until a minimum number of bytes has been
/// transferred, or until an error occurs.
#if defined(GENERATING_DOCUMENTATION)
unspecified transfer_at_least(std::size_t minimum);
#else
inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum)
{
return detail::transfer_at_least_t(minimum);
}
#endif
/*@}*/
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_COMPLETION_CONDITION_HPP

View File

@@ -0,0 +1,295 @@
//
// datagram_socket_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP
#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/epoll_reactor.hpp"
#include "asio/detail/kqueue_reactor.hpp"
#include "asio/detail/select_reactor.hpp"
#include "asio/detail/reactive_socket_service.hpp"
#include "asio/detail/win_iocp_socket_service.hpp"
namespace asio {
/// Default service implementation for a datagram socket.
template <typename Protocol>
class datagram_socket_service
: public asio::io_service::service
{
public:
/// The protocol type.
typedef Protocol protocol_type;
/// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
private:
// The type of the platform-specific implementation.
#if defined(ASIO_HAS_IOCP)
typedef detail::win_iocp_socket_service<Protocol> service_impl_type;
#elif defined(ASIO_HAS_EPOLL)
typedef detail::reactive_socket_service<
Protocol, detail::epoll_reactor<false> > service_impl_type;
#elif defined(ASIO_HAS_KQUEUE)
typedef detail::reactive_socket_service<
Protocol, detail::kqueue_reactor<false> > service_impl_type;
#else
typedef detail::reactive_socket_service<
Protocol, detail::select_reactor<false> > service_impl_type;
#endif
public:
/// The type of a datagram socket.
#if defined(GENERATING_DOCUMENTATION)
typedef implementation_defined implementation_type;
#else
typedef typename service_impl_type::implementation_type implementation_type;
#endif
/// The native socket type.
#if defined(GENERATING_DOCUMENTATION)
typedef implementation_defined native_type;
#else
typedef typename service_impl_type::native_type native_type;
#endif
/// Construct a new datagram socket service for the specified io_service.
explicit datagram_socket_service(asio::io_service& io_service)
: asio::io_service::service(io_service),
service_impl_(asio::use_service<service_impl_type>(io_service))
{
}
/// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
}
/// Construct a new datagram socket implementation.
void construct(implementation_type& impl)
{
service_impl_.construct(impl);
}
/// Destroy a datagram socket implementation.
void destroy(implementation_type& impl)
{
service_impl_.destroy(impl);
}
// Open a new datagram socket implementation.
template <typename Error_Handler>
void open(implementation_type& impl, const protocol_type& protocol,
Error_Handler error_handler)
{
if (protocol.type() == SOCK_DGRAM)
service_impl_.open(impl, protocol, error_handler);
else
error_handler(asio::error(asio::error::invalid_argument));
}
/// Assign an existing native socket to a datagram socket.
template <typename Error_Handler>
void assign(implementation_type& impl, const protocol_type& protocol,
const native_type& native_socket, Error_Handler error_handler)
{
service_impl_.assign(impl, protocol, native_socket, error_handler);
}
/// Close a datagram socket implementation.
template <typename Error_Handler>
void close(implementation_type& impl, Error_Handler error_handler)
{
service_impl_.close(impl, error_handler);
}
/// Get the native socket implementation.
native_type native(implementation_type& impl)
{
return service_impl_.native(impl);
}
/// Cancel all asynchronous operations associated with the socket.
template <typename Error_Handler>
void cancel(implementation_type& impl, Error_Handler error_handler)
{
service_impl_.cancel(impl, error_handler);
}
// Bind the datagram socket to the specified local endpoint.
template <typename Error_Handler>
void bind(implementation_type& impl, const endpoint_type& endpoint,
Error_Handler error_handler)
{
service_impl_.bind(impl, endpoint, error_handler);
}
/// Connect the datagram socket to the specified endpoint.
template <typename Error_Handler>
void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
Error_Handler error_handler)
{
service_impl_.connect(impl, peer_endpoint, error_handler);
}
/// Start an asynchronous connect.
template <typename Handler>
void async_connect(implementation_type& impl,
const endpoint_type& peer_endpoint, Handler handler)
{
service_impl_.async_connect(impl, peer_endpoint, handler);
}
/// Set a socket option.
template <typename Option, typename Error_Handler>
void set_option(implementation_type& impl, const Option& option,
Error_Handler error_handler)
{
service_impl_.set_option(impl, option, error_handler);
}
/// Get a socket option.
template <typename Option, typename Error_Handler>
void get_option(const implementation_type& impl, Option& option,
Error_Handler error_handler) const
{
service_impl_.get_option(impl, option, error_handler);
}
/// Perform an IO control command on the socket.
template <typename IO_Control_Command, typename Error_Handler>
void io_control(implementation_type& impl, IO_Control_Command& command,
Error_Handler error_handler)
{
service_impl_.io_control(impl, command, error_handler);
}
/// Get the local endpoint.
template <typename Error_Handler>
endpoint_type local_endpoint(const implementation_type& impl,
Error_Handler error_handler) const
{
endpoint_type endpoint;
service_impl_.get_local_endpoint(impl, endpoint, error_handler);
return endpoint;
}
/// Get the remote endpoint.
template <typename Error_Handler>
endpoint_type remote_endpoint(const implementation_type& impl,
Error_Handler error_handler) const
{
endpoint_type endpoint;
service_impl_.get_remote_endpoint(impl, endpoint, error_handler);
return endpoint;
}
/// Disable sends or receives on the socket.
template <typename Error_Handler>
void shutdown(implementation_type& impl, socket_base::shutdown_type what,
Error_Handler error_handler)
{
service_impl_.shutdown(impl, what, error_handler);
}
/// Send the given data to the peer.
template <typename Const_Buffers, typename Error_Handler>
std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
socket_base::message_flags flags, Error_Handler error_handler)
{
return service_impl_.send(impl, buffers, flags, error_handler);
}
/// Start an asynchronous send.
template <typename Const_Buffers, typename Handler>
void async_send(implementation_type& impl, const Const_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
service_impl_.async_send(impl, buffers, flags, handler);
}
/// Send a datagram to the specified endpoint.
template <typename Const_Buffers, typename Error_Handler>
std::size_t send_to(implementation_type& impl, const Const_Buffers& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
Error_Handler error_handler)
{
return service_impl_.send_to(impl, buffers, destination, flags,
error_handler);
}
/// Start an asynchronous send.
template <typename Const_Buffers, typename Handler>
void async_send_to(implementation_type& impl, const Const_Buffers& buffers,
const endpoint_type& destination, socket_base::message_flags flags,
Handler handler)
{
service_impl_.async_send_to(impl, buffers, destination, flags, handler);
}
/// Receive some data from the peer.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
socket_base::message_flags flags, Error_Handler error_handler)
{
return service_impl_.receive(impl, buffers, flags, error_handler);
}
/// Start an asynchronous receive.
template <typename Mutable_Buffers, typename Handler>
void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
socket_base::message_flags flags, Handler handler)
{
service_impl_.async_receive(impl, buffers, flags, handler);
}
/// Receive a datagram with the endpoint of the sender.
template <typename Mutable_Buffers, typename Error_Handler>
std::size_t receive_from(implementation_type& impl,
const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
socket_base::message_flags flags, Error_Handler error_handler)
{
return service_impl_.receive_from(impl, buffers, sender_endpoint, flags,
error_handler);
}
/// Start an asynchronous receive that will get the endpoint of the sender.
template <typename Mutable_Buffers, typename Handler>
void async_receive_from(implementation_type& impl,
const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
socket_base::message_flags flags, Handler handler)
{
service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags,
handler);
}
private:
// The service that provides the platform-specific implementation.
service_impl_type& service_impl_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP

View File

@@ -0,0 +1,37 @@
//
// deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DEADLINE_TIMER_HPP
#define ASIO_DEADLINE_TIMER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
#include "asio/detail/push_options.hpp"
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/basic_deadline_timer.hpp"
namespace asio {
/// Typedef for the typical usage of timer.
typedef basic_deadline_timer<boost::posix_time::ptime> deadline_timer;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DEADLINE_TIMER_HPP

View File

@@ -0,0 +1,152 @@
//
// deadline_timer_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP
#define ASIO_DEADLINE_TIMER_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/io_service.hpp"
#include "asio/time_traits.hpp"
#include "asio/detail/deadline_timer_service.hpp"
#include "asio/detail/epoll_reactor.hpp"
#include "asio/detail/kqueue_reactor.hpp"
#include "asio/detail/select_reactor.hpp"
namespace asio {
/// Default service implementation for a timer.
template <typename Time_Type,
typename Time_Traits = asio::time_traits<Time_Type> >
class deadline_timer_service
: public asio::io_service::service
{
public:
/// The time traits type.
typedef Time_Traits traits_type;
/// The time type.
typedef typename traits_type::time_type time_type;
/// The duration type.
typedef typename traits_type::duration_type duration_type;
private:
// The type of the platform-specific implementation.
#if defined(ASIO_HAS_IOCP)
typedef detail::deadline_timer_service<
traits_type, detail::select_reactor<true> > service_impl_type;
#elif defined(ASIO_HAS_EPOLL)
typedef detail::deadline_timer_service<
traits_type, detail::epoll_reactor<false> > service_impl_type;
#elif defined(ASIO_HAS_KQUEUE)
typedef detail::deadline_timer_service<
traits_type, detail::kqueue_reactor<false> > service_impl_type;
#else
typedef detail::deadline_timer_service<
traits_type, detail::select_reactor<false> > service_impl_type;
#endif
public:
/// The implementation type of the deadline timer.
#if defined(GENERATING_DOCUMENTATION)
typedef implementation_defined implementation_type;
#else
typedef typename service_impl_type::implementation_type implementation_type;
#endif
/// Construct a new timer service for the specified io_service.
explicit deadline_timer_service(asio::io_service& io_service)
: asio::io_service::service(io_service),
service_impl_(asio::use_service<service_impl_type>(io_service))
{
}
/// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
}
/// Construct a new timer implementation.
void construct(implementation_type& impl)
{
service_impl_.construct(impl);
}
/// Destroy a timer implementation.
void destroy(implementation_type& impl)
{
service_impl_.destroy(impl);
}
/// Cancel any asynchronous wait operations associated with the timer.
std::size_t cancel(implementation_type& impl)
{
return service_impl_.cancel(impl);
}
/// Get the expiry time for the timer as an absolute time.
time_type expires_at(const implementation_type& impl) const
{
return service_impl_.expires_at(impl);
}
/// Set the expiry time for the timer as an absolute time.
std::size_t expires_at(implementation_type& impl,
const time_type& expiry_time)
{
return service_impl_.expires_at(impl, expiry_time);
}
/// Get the expiry time for the timer relative to now.
duration_type expires_from_now(const implementation_type& impl) const
{
return service_impl_.expires_from_now(impl);
}
/// Set the expiry time for the timer relative to now.
std::size_t expires_from_now(implementation_type& impl,
const duration_type& expiry_time)
{
return service_impl_.expires_from_now(impl, expiry_time);
}
// Perform a blocking wait on the timer.
void wait(implementation_type& impl)
{
service_impl_.wait(impl);
}
// Start an asynchronous wait on the timer.
template <typename Handler>
void async_wait(implementation_type& impl, Handler handler)
{
service_impl_.async_wait(impl, handler);
}
private:
// The service that provides the platform-specific implementation.
service_impl_type& service_impl_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP

View File

@@ -0,0 +1,349 @@
//
// bind_handler.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_BIND_HANDLER_HPP
#define ASIO_DETAIL_BIND_HANDLER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
namespace asio {
namespace detail {
template <typename Handler, typename Arg1>
class binder1
{
public:
binder1(const Handler& handler, const Arg1& arg1)
: handler_(handler),
arg1_(arg1)
{
}
void operator()()
{
handler_(arg1_);
}
void operator()() const
{
handler_(arg1_);
}
//private:
Handler handler_;
Arg1 arg1_;
};
template <typename Handler, typename Arg1>
inline void* asio_handler_allocate(std::size_t size,
binder1<Handler, Arg1>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, &this_handler->handler_);
}
template <typename Handler, typename Arg1>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
binder1<Handler, Arg1>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, &this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1>
inline void asio_handler_invoke(const Function& function,
binder1<Handler, Arg1>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, &this_handler->handler_);
}
template <typename Handler, typename Arg1>
inline binder1<Handler, Arg1> bind_handler(const Handler& handler,
const Arg1& arg1)
{
return binder1<Handler, Arg1>(handler, arg1);
}
template <typename Handler, typename Arg1, typename Arg2>
class binder2
{
public:
binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2)
: handler_(handler),
arg1_(arg1),
arg2_(arg2)
{
}
void operator()()
{
handler_(arg1_, arg2_);
}
void operator()() const
{
handler_(arg1_, arg2_);
}
//private:
Handler handler_;
Arg1 arg1_;
Arg2 arg2_;
};
template <typename Handler, typename Arg1, typename Arg2>
inline void* asio_handler_allocate(std::size_t size,
binder2<Handler, Arg1, Arg2>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
binder2<Handler, Arg1, Arg2>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, &this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_invoke(const Function& function,
binder2<Handler, Arg1, Arg2>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2>
inline binder2<Handler, Arg1, Arg2> bind_handler(const Handler& handler,
const Arg1& arg1, const Arg2& arg2)
{
return binder2<Handler, Arg1, Arg2>(handler, arg1, arg2);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
class binder3
{
public:
binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
const Arg3& arg3)
: handler_(handler),
arg1_(arg1),
arg2_(arg2),
arg3_(arg3)
{
}
void operator()()
{
handler_(arg1_, arg2_, arg3_);
}
void operator()() const
{
handler_(arg1_, arg2_, arg3_);
}
//private:
Handler handler_;
Arg1 arg1_;
Arg2 arg2_;
Arg3 arg3_;
};
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
inline void* asio_handler_allocate(std::size_t size,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, &this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1, typename Arg2,
typename Arg3>
inline void asio_handler_invoke(const Function& function,
binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
inline binder3<Handler, Arg1, Arg2, Arg3> bind_handler(const Handler& handler,
const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
{
return binder3<Handler, Arg1, Arg2, Arg3>(handler, arg1, arg2, arg3);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
class binder4
{
public:
binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
const Arg3& arg3, const Arg4& arg4)
: handler_(handler),
arg1_(arg1),
arg2_(arg2),
arg3_(arg3),
arg4_(arg4)
{
}
void operator()()
{
handler_(arg1_, arg2_, arg3_, arg4_);
}
void operator()() const
{
handler_(arg1_, arg2_, arg3_, arg4_);
}
//private:
Handler handler_;
Arg1 arg1_;
Arg2 arg2_;
Arg3 arg3_;
Arg4 arg4_;
};
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
inline void* asio_handler_allocate(std::size_t size,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, &this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1, typename Arg2,
typename Arg3, typename Arg4>
inline void asio_handler_invoke(const Function& function,
binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
inline binder4<Handler, Arg1, Arg2, Arg3, Arg4> bind_handler(
const Handler& handler, const Arg1& arg1, const Arg2& arg2,
const Arg3& arg3, const Arg4& arg4)
{
return binder4<Handler, Arg1, Arg2, Arg3, Arg4>(handler, arg1, arg2, arg3,
arg4);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
class binder5
{
public:
binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
: handler_(handler),
arg1_(arg1),
arg2_(arg2),
arg3_(arg3),
arg4_(arg4),
arg5_(arg5)
{
}
void operator()()
{
handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
}
void operator()() const
{
handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
}
//private:
Handler handler_;
Arg1 arg1_;
Arg2 arg2_;
Arg3 arg3_;
Arg4 arg4_;
Arg5 arg5_;
};
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
inline void* asio_handler_allocate(std::size_t size,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, &this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1, typename Arg2,
typename Arg3, typename Arg4, typename Arg5>
inline void asio_handler_invoke(const Function& function,
binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
{
asio_handler_invoke_helpers::invoke(
function, &this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
inline binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5> bind_handler(
const Handler& handler, const Arg1& arg1, const Arg2& arg2,
const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
{
return binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>(handler, arg1, arg2,
arg3, arg4, arg5);
}
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_BIND_HANDLER_HPP

View File

@@ -0,0 +1,70 @@
//
// buffer_resize_guard.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <limits>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
// Helper class to manage buffer resizing in an exception safe way.
template <typename Buffer>
class buffer_resize_guard
{
public:
// Constructor.
buffer_resize_guard(Buffer& buffer)
: buffer_(buffer),
old_size_(buffer.size())
{
}
// Destructor rolls back the buffer resize unless commit was called.
~buffer_resize_guard()
{
if (old_size_
!= std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION())
{
buffer_.resize(old_size_);
}
}
// Commit the resize transaction.
void commit()
{
old_size_
= std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
}
private:
// The buffer being managed.
Buffer& buffer_;
// The size of the buffer at the time the guard was constructed.
size_t old_size_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP

View File

@@ -0,0 +1,127 @@
//
// buffered_stream_storage.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <vector>
#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
class buffered_stream_storage
{
public:
// The type of the bytes stored in the buffer.
typedef unsigned char byte_type;
// The type used for offsets into the buffer.
typedef std::size_t size_type;
// Constructor.
explicit buffered_stream_storage(std::size_t capacity)
: begin_offset_(0),
end_offset_(0),
buffer_(capacity)
{
}
/// Clear the buffer.
void clear()
{
begin_offset_ = 0;
end_offset_ = 0;
}
// Return a pointer to the beginning of the unread data.
byte_type* data()
{
return &buffer_[0] + begin_offset_;
}
// Return a pointer to the beginning of the unread data.
const byte_type* data() const
{
return &buffer_[0] + begin_offset_;
}
// Is there no unread data in the buffer.
bool empty() const
{
return begin_offset_ == end_offset_;
}
// Return the amount of unread data the is in the buffer.
size_type size() const
{
return end_offset_ - begin_offset_;
}
// Resize the buffer to the specified length.
void resize(size_type length)
{
assert(length <= capacity());
if (begin_offset_ + length <= capacity())
{
end_offset_ = begin_offset_ + length;
}
else
{
using namespace std; // For memmove.
memmove(&buffer_[0], &buffer_[0] + begin_offset_, size());
end_offset_ = length;
begin_offset_ = 0;
}
}
// Return the maximum size for data in the buffer.
size_type capacity() const
{
return buffer_.size();
}
// Consume multiple bytes from the beginning of the buffer.
void consume(size_type count)
{
assert(begin_offset_ + count <= end_offset_);
begin_offset_ += count;
if (empty())
clear();
}
private:
// The offset to the beginning of the unread data.
size_type begin_offset_;
// The offset to the end of the unread data.
size_type end_offset_;
// The data in the buffer.
std::vector<byte_type> buffer_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP

View File

@@ -0,0 +1,90 @@
//
// call_stack.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_CALL_STACK_HPP
#define ASIO_DETAIL_CALL_STACK_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/tss_ptr.hpp"
namespace asio {
namespace detail {
// Helper class to determine whether or not the current thread is inside an
// invocation of io_service::run() for a specified io_service object.
template <typename Owner>
class call_stack
{
public:
// Context class automatically pushes an owner on to the stack.
class context
: private noncopyable
{
public:
// Push the owner on to the stack.
explicit context(Owner* d)
: owner_(d),
next_(call_stack<Owner>::top_)
{
call_stack<Owner>::top_ = this;
}
// Pop the owner from the stack.
~context()
{
call_stack<Owner>::top_ = next_;
}
private:
friend class call_stack<Owner>;
// The owner associated with the context.
Owner* owner_;
// The next element in the stack.
context* next_;
};
friend class context;
// Determine whether the specified owner is on the stack.
static bool contains(Owner* d)
{
context* elem = top_;
while (elem)
{
if (elem->owner_ == d)
return true;
elem = elem->next_;
}
return false;
}
private:
// The top of the stack of calls for the current thread.
static tss_ptr<context> top_;
};
template <typename Owner>
tss_ptr<typename call_stack<Owner>::context>
call_stack<Owner>::top_;
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_CALL_STACK_HPP

View File

@@ -0,0 +1,150 @@
//
// const_buffers_iterator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
#define ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/buffer.hpp"
namespace asio {
namespace detail {
// A proxy iterator for a sub-range in a list of buffers.
template <typename Const_Buffers>
class const_buffers_iterator
: public boost::iterator_facade<const_buffers_iterator<Const_Buffers>,
const char, boost::bidirectional_traversal_tag>
{
public:
// Default constructor creates an iterator in an undefined state.
const_buffers_iterator()
{
}
// Create an iterator for the specified position.
const_buffers_iterator(const Const_Buffers& buffers, std::size_t position)
: begin_(buffers.begin()),
current_(buffers.begin()),
end_(buffers.end()),
position_(0)
{
while (current_ != end_)
{
current_buffer_ = *current_;
std::size_t buffer_size = asio::buffer_size(current_buffer_);
if (position - position_ < buffer_size)
{
current_buffer_position_ = position - position_;
position_ = position;
return;
}
position_ += buffer_size;
++current_;
}
current_buffer_ = asio::const_buffer();
current_buffer_position_ = 0;
}
std::size_t position() const
{
return position_;
}
private:
friend class boost::iterator_core_access;
void increment()
{
if (current_ == end_)
return;
++position_;
++current_buffer_position_;
if (current_buffer_position_ != asio::buffer_size(current_buffer_))
return;
++current_;
current_buffer_position_ = 0;
while (current_ != end_)
{
current_buffer_ = *current_;
if (asio::buffer_size(current_buffer_) > 0)
return;
++current_;
}
}
void decrement()
{
if (position_ == 0)
return;
--position_;
if (current_buffer_position_ != 0)
{
--current_buffer_position_;
return;
}
typename Const_Buffers::const_iterator iter = current_;
while (iter != begin_)
{
--iter;
asio::const_buffer buffer = *iter;
std::size_t buffer_size = asio::buffer_size(buffer);
if (buffer_size > 0)
{
current_ = iter;
current_buffer_ = buffer;
current_buffer_position_ = buffer_size - 1;
return;
}
}
}
bool equal(const const_buffers_iterator& other) const
{
return position_ == other.position_;
}
const char& dereference() const
{
return asio::buffer_cast<const char*>(
current_buffer_)[current_buffer_position_];
}
asio::const_buffer current_buffer_;
std::size_t current_buffer_position_;
typename Const_Buffers::const_iterator begin_;
typename Const_Buffers::const_iterator current_;
typename Const_Buffers::const_iterator end_;
std::size_t position_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP

View File

@@ -0,0 +1,196 @@
//
// consuming_buffers.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP
#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <algorithm>
#include <cstddef>
#include <boost/config.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
// A proxy iterator for a sub-range in a list of buffers.
template <typename Buffer, typename Buffer_Iterator>
class consuming_buffers_iterator
: public boost::iterator_facade<
consuming_buffers_iterator<Buffer, Buffer_Iterator>,
const Buffer,
boost::forward_traversal_tag>
{
public:
// Default constructor creates an end iterator.
consuming_buffers_iterator()
: at_end_(true)
{
}
// Construct with a buffer for the first entry and an iterator
// range for the remaining entries.
consuming_buffers_iterator(bool at_end, const Buffer& first,
Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder)
: at_end_(at_end),
first_(first),
begin_remainder_(begin_remainder),
end_remainder_(end_remainder)
{
}
private:
friend class boost::iterator_core_access;
void increment()
{
if (!at_end_)
{
if (begin_remainder_ == end_remainder_)
at_end_ = true;
else
first_ = *begin_remainder_++;
}
}
bool equal(const consuming_buffers_iterator& other) const
{
if (at_end_ && other.at_end_)
return true;
return !at_end_ && !other.at_end_
&& buffer_cast<const void*>(first_)
== buffer_cast<const void*>(other.first_)
&& buffer_size(first_) == buffer_size(other.first_)
&& begin_remainder_ == other.begin_remainder_
&& end_remainder_ == other.end_remainder_;
}
const Buffer& dereference() const
{
return first_;
}
bool at_end_;
Buffer first_;
Buffer_Iterator begin_remainder_;
Buffer_Iterator end_remainder_;
};
// A proxy for a sub-range in a list of buffers.
template <typename Buffer, typename Buffers>
class consuming_buffers
{
public:
// The type for each element in the list of buffers.
typedef Buffer value_type;
// A forward-only iterator type that may be used to read elements.
typedef consuming_buffers_iterator<Buffer, typename Buffers::const_iterator>
const_iterator;
// Construct to represent the entire list of buffers.
consuming_buffers(const Buffers& buffers)
: buffers_(buffers),
at_end_(buffers_.begin() == buffers_.end()),
first_(*buffers_.begin()),
begin_remainder_(buffers_.begin())
{
if (!at_end_)
++begin_remainder_;
}
// Copy constructor.
consuming_buffers(const consuming_buffers& other)
: buffers_(other.buffers_),
at_end_(other.at_end_),
first_(other.first_),
begin_remainder_(buffers_.begin())
{
typename Buffers::const_iterator first = other.buffers_.begin();
typename Buffers::const_iterator second = other.begin_remainder_;
std::advance(begin_remainder_, std::distance(first, second));
}
// Assignment operator.
consuming_buffers& operator=(const consuming_buffers& other)
{
buffers_ = other.buffers_;
at_end_ = other.at_end_;
first_ = other.first_;
begin_remainder_ = buffers_.begin();
typename Buffers::const_iterator first = other.buffers_.begin();
typename Buffers::const_iterator second = other.begin_remainder_;
std::advance(begin_remainder_, std::distance(first, second));
return *this;
}
// Get a forward-only iterator to the first element.
const_iterator begin() const
{
return const_iterator(at_end_, first_, begin_remainder_, buffers_.end());
}
// Get a forward-only iterator for one past the last element.
const_iterator end() const
{
return const_iterator();
}
// Consume the specified number of bytes from the buffers.
void consume(std::size_t size)
{
// Remove buffers from the start until the specified size is reached.
while (size > 0 && !at_end_)
{
if (buffer_size(first_) <= size)
{
size -= buffer_size(first_);
if (begin_remainder_ == buffers_.end())
at_end_ = true;
else
first_ = *begin_remainder_++;
}
else
{
first_ = first_ + size;
size = 0;
}
}
// Remove any more empty buffers at the start.
while (!at_end_ && buffer_size(first_) == 0)
{
if (begin_remainder_ == buffers_.end())
at_end_ = true;
else
first_ = *begin_remainder_++;
}
}
private:
Buffers buffers_;
bool at_end_;
Buffer first_;
typename Buffers::const_iterator begin_remainder_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP

View File

@@ -0,0 +1,182 @@
//
// deadline_timer_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_queue.hpp"
namespace asio {
namespace detail {
template <typename Time_Traits, typename Timer_Scheduler>
class deadline_timer_service
: public asio::io_service::service
{
public:
// The time type.
typedef typename Time_Traits::time_type time_type;
// The duration type.
typedef typename Time_Traits::duration_type duration_type;
// The implementation type of the timer. This type is dependent on the
// underlying implementation of the timer service.
struct implementation_type
: private asio::detail::noncopyable
{
time_type expiry;
bool might_have_pending_waits;
};
// Constructor.
deadline_timer_service(asio::io_service& io_service)
: asio::io_service::service(io_service),
scheduler_(asio::use_service<Timer_Scheduler>(io_service))
{
scheduler_.add_timer_queue(timer_queue_);
}
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
}
// Construct a new timer implementation.
void construct(implementation_type& impl)
{
impl.expiry = time_type();
impl.might_have_pending_waits = false;
}
// Destroy a timer implementation.
void destroy(implementation_type& impl)
{
cancel(impl);
}
// Cancel any asynchronous wait operations associated with the timer.
std::size_t cancel(implementation_type& impl)
{
if (!impl.might_have_pending_waits)
return 0;
std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl);
impl.might_have_pending_waits = false;
return count;
}
// Get the expiry time for the timer as an absolute time.
time_type expires_at(const implementation_type& impl) const
{
return impl.expiry;
}
// Set the expiry time for the timer as an absolute time.
std::size_t expires_at(implementation_type& impl,
const time_type& expiry_time)
{
std::size_t count = cancel(impl);
impl.expiry = expiry_time;
return count;
}
// Get the expiry time for the timer relative to now.
duration_type expires_from_now(const implementation_type& impl) const
{
return Time_Traits::subtract(expires_at(impl), Time_Traits::now());
}
// Set the expiry time for the timer relative to now.
std::size_t expires_from_now(implementation_type& impl,
const duration_type& expiry_time)
{
return expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time));
}
// Perform a blocking wait on the timer.
void wait(implementation_type& impl)
{
time_type now = Time_Traits::now();
while (Time_Traits::less_than(now, impl.expiry))
{
boost::posix_time::time_duration timeout =
Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now));
::timeval tv;
tv.tv_sec = timeout.total_seconds();
tv.tv_usec = timeout.total_microseconds() % 1000000;
socket_ops::select(0, 0, 0, 0, &tv);
now = Time_Traits::now();
}
}
template <typename Handler>
class wait_handler
{
public:
wait_handler(asio::io_service& io_service, Handler handler)
: io_service_(io_service),
work_(io_service),
handler_(handler)
{
}
void operator()(int result)
{
asio::error e(result);
io_service_.post(detail::bind_handler(handler_, e));
}
private:
asio::io_service& io_service_;
asio::io_service::work work_;
Handler handler_;
};
// Start an asynchronous wait on the timer.
template <typename Handler>
void async_wait(implementation_type& impl, Handler handler)
{
impl.might_have_pending_waits = true;
scheduler_.schedule_timer(timer_queue_, impl.expiry,
wait_handler<Handler>(io_service(), handler), &impl);
}
private:
// The queue of timers.
timer_queue<Time_Traits> timer_queue_;
// The object that schedules and executes timers. Usually a reactor.
Timer_Scheduler& scheduler_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP

View File

@@ -0,0 +1,593 @@
//
// epoll_reactor.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP
#define ASIO_DETAIL_EPOLL_REACTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/epoll_reactor_fwd.hpp"
#if defined(ASIO_HAS_EPOLL)
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <vector>
#include <sys/epoll.h>
#include <boost/config.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/throw_exception.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/io_service.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/hash_map.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/task_io_service.hpp"
#include "asio/detail/thread.hpp"
#include "asio/detail/reactor_op_queue.hpp"
#include "asio/detail/select_interrupter.hpp"
#include "asio/detail/signal_blocker.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_queue.hpp"
namespace asio {
namespace detail {
template <bool Own_Thread>
class epoll_reactor
: public asio::io_service::service
{
public:
// Constructor.
epoll_reactor(asio::io_service& io_service)
: asio::io_service::service(io_service),
mutex_(),
epoll_fd_(do_epoll_create()),
wait_in_progress_(false),
interrupter_(),
read_op_queue_(),
write_op_queue_(),
except_op_queue_(),
pending_cancellations_(),
stop_thread_(false),
thread_(0),
shutdown_(false)
{
// Start the reactor's internal thread only if needed.
if (Own_Thread)
{
asio::detail::signal_blocker sb;
thread_ = new asio::detail::thread(
bind_handler(&epoll_reactor::call_run_thread, this));
}
// Add the interrupter's descriptor to epoll.
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLIN | EPOLLERR;
ev.data.fd = interrupter_.read_descriptor();
epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev);
}
// Destructor.
~epoll_reactor()
{
shutdown_service();
close(epoll_fd_);
}
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
asio::detail::mutex::scoped_lock lock(mutex_);
shutdown_ = true;
stop_thread_ = true;
lock.unlock();
if (thread_)
{
interrupter_.interrupt();
thread_->join();
delete thread_;
thread_ = 0;
}
read_op_queue_.destroy_operations();
write_op_queue_.destroy_operations();
except_op_queue_.destroy_operations();
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->destroy_timers();
timer_queues_.clear();
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type descriptor)
{
// No need to lock according to epoll documentation.
epoll_event ev = { 0, { 0 } };
ev.events = 0;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
if (result != 0)
return errno;
return 0;
}
// Start a new read operation. The handler object will be invoked when the
// given descriptor is ready to be read, or an error has occurred.
template <typename Handler>
void start_read_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (!read_op_queue_.has_operation(descriptor))
if (handler(0))
return;
if (read_op_queue_.enqueue_operation(descriptor, handler))
{
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
if (write_op_queue_.has_operation(descriptor))
ev.events |= EPOLLOUT;
if (except_op_queue_.has_operation(descriptor))
ev.events |= EPOLLPRI;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
if (result != 0)
{
int error = errno;
read_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start a new write operation. The handler object will be invoked when the
// given descriptor is ready to be written, or an error has occurred.
template <typename Handler>
void start_write_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (!write_op_queue_.has_operation(descriptor))
if (handler(0))
return;
if (write_op_queue_.enqueue_operation(descriptor, handler))
{
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP;
if (read_op_queue_.has_operation(descriptor))
ev.events |= EPOLLIN;
if (except_op_queue_.has_operation(descriptor))
ev.events |= EPOLLPRI;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
if (result != 0)
{
int error = errno;
write_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start a new exception operation. The handler object will be invoked when
// the given descriptor has exception information, or an error has occurred.
template <typename Handler>
void start_except_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (except_op_queue_.enqueue_operation(descriptor, handler))
{
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP;
if (read_op_queue_.has_operation(descriptor))
ev.events |= EPOLLIN;
if (write_op_queue_.has_operation(descriptor))
ev.events |= EPOLLOUT;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
if (result != 0)
{
int error = errno;
except_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start new write and exception operations. The handler object will be
// invoked when the given descriptor is ready for writing or has exception
// information available, or an error has occurred.
template <typename Handler>
void start_write_and_except_ops(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler);
need_mod = except_op_queue_.enqueue_operation(descriptor, handler)
&& need_mod;
if (need_mod)
{
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP;
if (read_op_queue_.has_operation(descriptor))
ev.events |= EPOLLIN;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
if (result != 0)
{
int error = errno;
write_op_queue_.dispatch_all_operations(descriptor, error);
except_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
void cancel_ops(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
cancel_ops_unlocked(descriptor);
}
// Enqueue cancellation of all operations associated with the given
// descriptor. The handlers associated with the descriptor will be invoked
// with the operation_aborted error. This function does not acquire the
// epoll_reactor's mutex, and so should only be used from within a reactor
// handler.
void enqueue_cancel_ops_unlocked(socket_type descriptor)
{
pending_cancellations_.push_back(descriptor);
}
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
void close_descriptor(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
// Remove the descriptor from epoll.
epoll_event ev = { 0, { 0 } };
epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev);
// Cancel any outstanding operations associated with the descriptor.
cancel_ops_unlocked(descriptor);
}
// Add a new timer queue to the reactor.
template <typename Time_Traits>
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
{
asio::detail::mutex::scoped_lock lock(mutex_);
timer_queues_.push_back(&timer_queue);
}
// Schedule a timer in the given timer queue to expire at the specified
// absolute time. The handler object will be invoked when the timer expires.
template <typename Time_Traits, typename Handler>
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
const typename Time_Traits::time_type& time, Handler handler, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (timer_queue.enqueue_timer(time, handler, token))
interrupter_.interrupt();
}
// Cancel the timer associated with the given token. Returns the number of
// handlers that have been posted or dispatched.
template <typename Time_Traits>
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
return timer_queue.cancel_timer(token);
}
private:
friend class task_io_service<epoll_reactor<Own_Thread> >;
// Run epoll once until interrupted or events are ready to be dispatched.
void run(bool block)
{
asio::detail::mutex::scoped_lock lock(mutex_);
// Dispatch any operation cancellations that were made while the select
// loop was not running.
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
except_op_queue_.dispatch_cancellations();
// Check if the thread is supposed to stop.
if (stop_thread_)
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
// We can return immediately if there's no work to do and the reactor is
// not supposed to block.
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
&& except_op_queue_.empty() && all_timer_queues_are_empty())
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
int timeout = block ? get_timeout() : 0;
wait_in_progress_ = true;
lock.unlock();
// Block on the epoll descriptor.
epoll_event events[128];
int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
lock.lock();
wait_in_progress_ = false;
// Block signals while dispatching operations.
asio::detail::signal_blocker sb;
// Dispatch the waiting events.
for (int i = 0; i < num_events; ++i)
{
int descriptor = events[i].data.fd;
if (descriptor == interrupter_.read_descriptor())
{
interrupter_.reset();
}
else
{
if (events[i].events & (EPOLLERR | EPOLLHUP))
{
except_op_queue_.dispatch_all_operations(descriptor, 0);
read_op_queue_.dispatch_all_operations(descriptor, 0);
write_op_queue_.dispatch_all_operations(descriptor, 0);
epoll_event ev = { 0, { 0 } };
ev.events = 0;
ev.data.fd = descriptor;
epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
}
else
{
bool more_reads = false;
bool more_writes = false;
bool more_except = false;
// Exception operations must be processed first to ensure that any
// out-of-band data is read before normal data.
if (events[i].events & EPOLLPRI)
more_except = except_op_queue_.dispatch_operation(descriptor, 0);
else
more_except = except_op_queue_.has_operation(descriptor);
if (events[i].events & EPOLLIN)
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
else
more_reads = read_op_queue_.has_operation(descriptor);
if (events[i].events & EPOLLOUT)
more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
else
more_writes = write_op_queue_.has_operation(descriptor);
epoll_event ev = { 0, { 0 } };
ev.events = EPOLLERR | EPOLLHUP;
if (more_reads)
ev.events |= EPOLLIN;
if (more_writes)
ev.events |= EPOLLOUT;
if (more_except)
ev.events |= EPOLLPRI;
ev.data.fd = descriptor;
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
if (result != 0)
{
int error = errno;
read_op_queue_.dispatch_all_operations(descriptor, error);
write_op_queue_.dispatch_all_operations(descriptor, error);
except_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
}
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
except_op_queue_.dispatch_cancellations();
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->dispatch_timers();
// Issue any pending cancellations.
for (size_t i = 0; i < pending_cancellations_.size(); ++i)
cancel_ops_unlocked(pending_cancellations_[i]);
pending_cancellations_.clear();
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
}
// Run the select loop in the thread.
void run_thread()
{
asio::detail::mutex::scoped_lock lock(mutex_);
while (!stop_thread_)
{
lock.unlock();
run(true);
lock.lock();
}
}
// Entry point for the select loop thread.
static void call_run_thread(epoll_reactor* reactor)
{
reactor->run_thread();
}
// Interrupt the select loop.
void interrupt()
{
interrupter_.interrupt();
}
// The hint to pass to epoll_create to size its data structures.
enum { epoll_size = 20000 };
// Create the epoll file descriptor. Throws an exception if the descriptor
// cannot be created.
static int do_epoll_create()
{
int fd = epoll_create(epoll_size);
if (fd == -1)
{
system_exception e("epoll", errno);
boost::throw_exception(e);
}
return fd;
}
// Check if all timer queues are empty.
bool all_timer_queues_are_empty() const
{
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
if (!timer_queues_[i]->empty())
return false;
return true;
}
// Get the timeout value for the epoll_wait call. The timeout value is
// returned as a number of milliseconds. A return value of -1 indicates
// that epoll_wait should block indefinitely.
int get_timeout()
{
if (all_timer_queues_are_empty())
return -1;
// By default we will wait no longer than 5 minutes. This will ensure that
// any changes to the system clock are detected after no longer than this.
boost::posix_time::time_duration minimum_wait_duration
= boost::posix_time::minutes(5);
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
{
boost::posix_time::time_duration wait_duration
= timer_queues_[i]->wait_duration();
if (wait_duration < minimum_wait_duration)
minimum_wait_duration = wait_duration;
}
if (minimum_wait_duration > boost::posix_time::time_duration())
{
return minimum_wait_duration.total_milliseconds();
}
else
{
return 0;
}
}
// Cancel all operations associated with the given descriptor. The do_cancel
// function of the handler objects will be invoked. This function does not
// acquire the epoll_reactor's mutex.
void cancel_ops_unlocked(socket_type descriptor)
{
bool interrupt = read_op_queue_.cancel_operations(descriptor);
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
if (interrupt)
interrupter_.interrupt();
}
// Mutex to protect access to internal data.
asio::detail::mutex mutex_;
// The epoll file descriptor.
int epoll_fd_;
// Whether the epoll_wait call is currently in progress
bool wait_in_progress_;
// The interrupter is used to break a blocking epoll_wait call.
select_interrupter interrupter_;
// The queue of read operations.
reactor_op_queue<socket_type> read_op_queue_;
// The queue of write operations.
reactor_op_queue<socket_type> write_op_queue_;
// The queue of except operations.
reactor_op_queue<socket_type> except_op_queue_;
// The timer queues.
std::vector<timer_queue_base*> timer_queues_;
// The descriptors that are pending cancellation.
std::vector<socket_type> pending_cancellations_;
// Does the reactor loop thread need to stop.
bool stop_thread_;
// The thread that is running the reactor loop.
asio::detail::thread* thread_;
// Whether the service has been shut down.
bool shutdown_;
};
} // namespace detail
} // namespace asio
#endif // defined(ASIO_HAS_EPOLL)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP

View File

@@ -0,0 +1,47 @@
//
// epoll_reactor_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#if !defined(ASIO_DISABLE_EPOLL)
#if defined(__linux__) // This service is only supported on Linux.
#include "asio/detail/push_options.hpp"
#include <linux/version.h>
#include "asio/detail/pop_options.hpp"
#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45.
// Define this to indicate that epoll is supported on the target platform.
#define ASIO_HAS_EPOLL 1
namespace asio {
namespace detail {
template <bool Own_Thread>
class epoll_reactor;
} // namespace detail
} // namespace asio
#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45)
#endif // defined(__linux__)
#endif // !defined(ASIO_DISABLE_EPOLL)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP

View File

@@ -0,0 +1,50 @@
//
// event.hpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_EVENT_HPP
#define ASIO_DETAIL_EVENT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
# include "asio/detail/null_event.hpp"
#elif defined(BOOST_WINDOWS)
# include "asio/detail/win_event.hpp"
#elif defined(BOOST_HAS_PTHREADS)
# include "asio/detail/posix_event.hpp"
#else
# error Only Windows and POSIX are supported!
#endif
namespace asio {
namespace detail {
#if !defined(BOOST_HAS_THREADS)
typedef null_event event;
#elif defined(BOOST_WINDOWS)
typedef win_event event;
#elif defined(BOOST_HAS_PTHREADS)
typedef posix_event event;
#endif
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_EVENT_HPP

View File

@@ -0,0 +1,41 @@
//
// fd_set_adapter.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP
#define ASIO_DETAIL_FD_SET_ADAPTER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/posix_fd_set_adapter.hpp"
#include "asio/detail/win_fd_set_adapter.hpp"
namespace asio {
namespace detail {
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
typedef win_fd_set_adapter fd_set_adapter;
#else
typedef posix_fd_set_adapter fd_set_adapter;
#endif
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP

View File

@@ -0,0 +1,256 @@
//
// handler_alloc_helpers.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/detail/workaround.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/handler_alloc_hook.hpp"
#include "asio/detail/noncopyable.hpp"
// Calls to asio_handler_allocate and asio_handler_deallocate must be made from
// a namespace that does not contain any overloads of these functions. The
// asio_handler_alloc_helpers namespace is defined here for that purpose.
namespace asio_handler_alloc_helpers {
template <typename Handler>
inline void* allocate(std::size_t s, Handler* h)
{
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
return ::operator new(s);
#else
using namespace asio;
return asio_handler_allocate(s, h);
#endif
}
template <typename Handler>
inline void deallocate(void* p, std::size_t s, Handler* h)
{
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
::operator delete(p);
#else
using namespace asio;
asio_handler_deallocate(p, s, h);
#endif
}
} // namespace asio_handler_alloc_helpers
namespace asio {
namespace detail {
// Traits for handler allocation.
template <typename Handler, typename Object>
struct handler_alloc_traits
{
typedef Handler handler_type;
typedef Object value_type;
typedef Object* pointer_type;
BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object));
};
template <typename Alloc_Traits>
class handler_ptr;
// Helper class to provide RAII on uninitialised handler memory.
template <typename Alloc_Traits>
class raw_handler_ptr
: private noncopyable
{
public:
typedef typename Alloc_Traits::handler_type handler_type;
typedef typename Alloc_Traits::value_type value_type;
typedef typename Alloc_Traits::pointer_type pointer_type;
BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
// Constructor allocates the memory.
raw_handler_ptr(handler_type& handler)
: handler_(handler),
pointer_(static_cast<pointer_type>(
asio_handler_alloc_helpers::allocate(value_size, &handler_)))
{
}
// Destructor automatically deallocates memory, unless it has been stolen by
// a handler_ptr object.
~raw_handler_ptr()
{
if (pointer_)
asio_handler_alloc_helpers::deallocate(
pointer_, value_size, &handler_);
}
private:
friend class handler_ptr<Alloc_Traits>;
handler_type& handler_;
pointer_type pointer_;
};
// Helper class to provide RAII on uninitialised handler memory.
template <typename Alloc_Traits>
class handler_ptr
: private noncopyable
{
public:
typedef typename Alloc_Traits::handler_type handler_type;
typedef typename Alloc_Traits::value_type value_type;
typedef typename Alloc_Traits::pointer_type pointer_type;
BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type;
// Take ownership of existing memory.
handler_ptr(handler_type& handler, pointer_type pointer)
: handler_(handler),
pointer_(pointer)
{
}
// Construct object in raw memory and take ownership if construction succeeds.
handler_ptr(raw_ptr_type& raw_ptr)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type)
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
typename Arg5>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
Arg5& a5)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
typename Arg5, typename Arg6>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
Arg5& a5, Arg6& a6)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
typename Arg5, typename Arg6, typename Arg7>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
Arg5& a5, Arg6& a6, Arg7& a7)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7))
{
raw_ptr.pointer_ = 0;
}
// Construct object in raw memory and take ownership if construction succeeds.
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
typename Arg5, typename Arg6, typename Arg7, typename Arg8>
handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8)
: handler_(raw_ptr.handler_),
pointer_(new (raw_ptr.pointer_) value_type(
a1, a2, a3, a4, a5, a6, a7, a8))
{
raw_ptr.pointer_ = 0;
}
// Destructor automatically deallocates memory, unless it has been released.
~handler_ptr()
{
reset();
}
// Get the memory.
pointer_type get() const
{
return pointer_;
}
// Release ownership of the memory.
pointer_type release()
{
pointer_type tmp = pointer_;
pointer_ = 0;
return tmp;
}
// Explicitly destroy and deallocate the memory.
void reset()
{
if (pointer_)
{
pointer_->value_type::~value_type();
asio_handler_alloc_helpers::deallocate(
pointer_, value_size, &handler_);
pointer_ = 0;
}
}
private:
handler_type& handler_;
pointer_type pointer_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP

View File

@@ -0,0 +1,47 @@
//
// handler_invoke_helpers.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/detail/workaround.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/handler_invoke_hook.hpp"
// Calls to asio_handler_invoke must be made from a namespace that does not
// contain overloads of this function. The asio_handler_invoke_helpers
// namespace is defined here for that purpose.
namespace asio_handler_invoke_helpers {
template <typename Function, typename Context>
inline void invoke(const Function& function, Context* context)
{
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
Function tmp(function);
tmp();
#else
using namespace asio;
asio_handler_invoke(function, context);
#endif
}
} // namespace asio_handler_invoke_helpers
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP

View File

@@ -0,0 +1,197 @@
//
// hash_map.hpp
// ~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_HASH_MAP_HPP
#define ASIO_DETAIL_HASH_MAP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cassert>
#include <list>
#include <utility>
#include <boost/functional/hash.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
using boost::hash_value;
template <typename K, typename V>
class hash_map
: private noncopyable
{
public:
// The type of a value in the map.
typedef std::pair<const K, V> value_type;
// The type of a non-const iterator over the hash map.
typedef typename std::list<value_type>::iterator iterator;
// The type of a const iterator over the hash map.
typedef typename std::list<value_type>::const_iterator const_iterator;
// Constructor.
hash_map()
{
// Initialise all buckets to empty.
for (size_t i = 0; i < num_buckets; ++i)
buckets_[i].first = buckets_[i].last = values_.end();
}
// Get an iterator for the beginning of the map.
iterator begin()
{
return values_.begin();
}
// Get an iterator for the beginning of the map.
const_iterator begin() const
{
return values_.begin();
}
// Get an iterator for the end of the map.
iterator end()
{
return values_.end();
}
// Get an iterator for the end of the map.
const_iterator end() const
{
return values_.end();
}
// Check whether the map is empty.
bool empty() const
{
return values_.empty();
}
// Find an entry in the map.
iterator find(const K& k)
{
size_t bucket = hash_value(k) % num_buckets;
iterator it = buckets_[bucket].first;
if (it == values_.end())
return values_.end();
iterator end = buckets_[bucket].last;
++end;
while (it != end)
{
if (it->first == k)
return it;
++it;
}
return values_.end();
}
// Find an entry in the map.
const_iterator find(const K& k) const
{
size_t bucket = hash_value(k) % num_buckets;
const_iterator it = buckets_[bucket].first;
if (it == values_.end())
return it;
const_iterator end = buckets_[bucket].last;
++end;
while (it != end)
{
if (it->first == k)
return it;
++it;
}
return values_.end();
}
// Insert a new entry into the map.
std::pair<iterator, bool> insert(const value_type& v)
{
size_t bucket = hash_value(v.first) % num_buckets;
iterator it = buckets_[bucket].first;
if (it == values_.end())
{
buckets_[bucket].first = buckets_[bucket].last =
values_.insert(values_.end(), v);
return std::pair<iterator, bool>(buckets_[bucket].last, true);
}
iterator end = buckets_[bucket].last;
++end;
while (it != end)
{
if (it->first == v.first)
return std::pair<iterator, bool>(it, false);
++it;
}
buckets_[bucket].last = values_.insert(end, v);
return std::pair<iterator, bool>(buckets_[bucket].last, true);
}
// Erase an entry from the map.
void erase(iterator it)
{
assert(it != values_.end());
size_t bucket = hash_value(it->first) % num_buckets;
bool is_first = (it == buckets_[bucket].first);
bool is_last = (it == buckets_[bucket].last);
if (is_first && is_last)
buckets_[bucket].first = buckets_[bucket].last = values_.end();
else if (is_first)
++buckets_[bucket].first;
else if (is_last)
--buckets_[bucket].last;
values_.erase(it);
}
// Remove all entries from the map.
void clear()
{
// Clear the values.
values_.clear();
// Initialise all buckets to empty.
for (size_t i = 0; i < num_buckets; ++i)
buckets_[i].first = buckets_[i].last = values_.end();
}
private:
// The list of all values in the hash map.
std::list<value_type> values_;
// The type for a bucket in the hash table.
struct bucket_type
{
iterator first;
iterator last;
};
// The number of buckets in the hash.
enum { num_buckets = 1021 };
// The buckets in the hash.
bucket_type buckets_[num_buckets];
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_HASH_MAP_HPP

View File

@@ -0,0 +1,137 @@
//
// io_control.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_IO_CONTROL_HPP
#define ASIO_DETAIL_IO_CONTROL_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/socket_types.hpp"
namespace asio {
namespace detail {
namespace io_control {
// IO control command for non-blocking I/O.
class non_blocking_io
{
public:
// Default constructor.
non_blocking_io()
: value_(0)
{
}
// Construct with a specific command value.
non_blocking_io(bool value)
: value_(value ? 1 : 0)
{
}
// Get the name of the IO control command.
int name() const
{
return FIONBIO;
}
// Set the value of the I/O control command.
void set(bool value)
{
value_ = value ? 1 : 0;
}
// Get the current value of the I/O control command.
bool get() const
{
return value_ != 0;
}
// Get the address of the command data.
detail::ioctl_arg_type* data()
{
return &value_;
}
// Get the address of the command data.
const detail::ioctl_arg_type* data() const
{
return &value_;
}
private:
detail::ioctl_arg_type value_;
};
// I/O control command for getting number of bytes available.
class bytes_readable
{
public:
// Default constructor.
bytes_readable()
: value_(0)
{
}
// Construct with a specific command value.
bytes_readable(std::size_t value)
: value_(value)
{
}
// Get the name of the IO control command.
int name() const
{
return FIONREAD;
}
// Set the value of the I/O control command.
void set(std::size_t value)
{
value_ = static_cast<detail::ioctl_arg_type>(value);
}
// Get the current value of the I/O control command.
std::size_t get() const
{
return static_cast<std::size_t>(value_);
}
// Get the address of the command data.
detail::ioctl_arg_type* data()
{
return &value_;
}
// Get the address of the command data.
const detail::ioctl_arg_type* data() const
{
return &value_;
}
private:
detail::ioctl_arg_type value_;
};
} // namespace io_control
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_IO_CONTROL_HPP

View File

@@ -0,0 +1,596 @@
//
// kqueue_reactor.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP
#define ASIO_DETAIL_KQUEUE_REACTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/kqueue_reactor_fwd.hpp"
#if defined(ASIO_HAS_KQUEUE)
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <vector>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <boost/config.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/throw_exception.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/io_service.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/task_io_service.hpp"
#include "asio/detail/thread.hpp"
#include "asio/detail/reactor_op_queue.hpp"
#include "asio/detail/select_interrupter.hpp"
#include "asio/detail/signal_blocker.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_queue.hpp"
// Older versions of Mac OS X may not define EV_OOBAND.
#if !defined(EV_OOBAND)
# define EV_OOBAND EV_FLAG1
#endif // !defined(EV_OOBAND)
namespace asio {
namespace detail {
template <bool Own_Thread>
class kqueue_reactor
: public asio::io_service::service
{
public:
// Constructor.
kqueue_reactor(asio::io_service& io_service)
: asio::io_service::service(io_service),
mutex_(),
kqueue_fd_(do_kqueue_create()),
wait_in_progress_(false),
interrupter_(),
read_op_queue_(),
write_op_queue_(),
except_op_queue_(),
pending_cancellations_(),
stop_thread_(false),
thread_(0),
shutdown_(false)
{
// Start the reactor's internal thread only if needed.
if (Own_Thread)
{
asio::detail::signal_blocker sb;
thread_ = new asio::detail::thread(
bind_handler(&kqueue_reactor::call_run_thread, this));
}
// Add the interrupter's descriptor to the kqueue.
struct kevent event;
EV_SET(&event, interrupter_.read_descriptor(),
EVFILT_READ, EV_ADD, 0, 0, 0);
::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
}
// Destructor.
~kqueue_reactor()
{
shutdown_service();
close(kqueue_fd_);
}
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
asio::detail::mutex::scoped_lock lock(mutex_);
shutdown_ = true;
stop_thread_ = true;
lock.unlock();
if (thread_)
{
interrupter_.interrupt();
thread_->join();
delete thread_;
thread_ = 0;
}
read_op_queue_.destroy_operations();
write_op_queue_.destroy_operations();
except_op_queue_.destroy_operations();
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->destroy_timers();
timer_queues_.clear();
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type)
{
return 0;
}
// Start a new read operation. The handler object will be invoked when the
// given descriptor is ready to be read, or an error has occurred.
template <typename Handler>
void start_read_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (!read_op_queue_.has_operation(descriptor))
if (handler(0))
return;
if (read_op_queue_.enqueue_operation(descriptor, handler))
{
struct kevent event;
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
read_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start a new write operation. The handler object will be invoked when the
// given descriptor is ready to be written, or an error has occurred.
template <typename Handler>
void start_write_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (!write_op_queue_.has_operation(descriptor))
if (handler(0))
return;
if (write_op_queue_.enqueue_operation(descriptor, handler))
{
struct kevent event;
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
write_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start a new exception operation. The handler object will be invoked when
// the given descriptor has exception information, or an error has occurred.
template <typename Handler>
void start_except_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (except_op_queue_.enqueue_operation(descriptor, handler))
{
struct kevent event;
if (read_op_queue_.has_operation(descriptor))
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
else
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
except_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Start new write and exception operations. The handler object will be
// invoked when the given descriptor is ready for writing or has exception
// information available, or an error has occurred.
template <typename Handler>
void start_write_and_except_ops(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (shutdown_)
return;
if (write_op_queue_.enqueue_operation(descriptor, handler))
{
struct kevent event;
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
write_op_queue_.dispatch_all_operations(descriptor, error);
}
}
if (except_op_queue_.enqueue_operation(descriptor, handler))
{
struct kevent event;
if (read_op_queue_.has_operation(descriptor))
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
else
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
except_op_queue_.dispatch_all_operations(descriptor, error);
write_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
void cancel_ops(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
cancel_ops_unlocked(descriptor);
}
// Enqueue cancellation of all operations associated with the given
// descriptor. The handlers associated with the descriptor will be invoked
// with the operation_aborted error. This function does not acquire the
// kqueue_reactor's mutex, and so should only be used from within a reactor
// handler.
void enqueue_cancel_ops_unlocked(socket_type descriptor)
{
pending_cancellations_.push_back(descriptor);
}
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
void close_descriptor(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
// Remove the descriptor from kqueue.
struct kevent event[2];
EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
::kevent(kqueue_fd_, event, 2, 0, 0, 0);
// Cancel any outstanding operations associated with the descriptor.
cancel_ops_unlocked(descriptor);
}
// Add a new timer queue to the reactor.
template <typename Time_Traits>
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
{
asio::detail::mutex::scoped_lock lock(mutex_);
timer_queues_.push_back(&timer_queue);
}
// Schedule a timer in the given timer queue to expire at the specified
// absolute time. The handler object will be invoked when the timer expires.
template <typename Time_Traits, typename Handler>
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
const typename Time_Traits::time_type& time, Handler handler, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (timer_queue.enqueue_timer(time, handler, token))
interrupter_.interrupt();
}
// Cancel the timer associated with the given token. Returns the number of
// handlers that have been posted or dispatched.
template <typename Time_Traits>
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
return timer_queue.cancel_timer(token);
}
private:
friend class task_io_service<kqueue_reactor<Own_Thread> >;
// Run the kqueue loop.
void run(bool block)
{
asio::detail::mutex::scoped_lock lock(mutex_);
// Dispatch any operation cancellations that were made while the select
// loop was not running.
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
except_op_queue_.dispatch_cancellations();
// Check if the thread is supposed to stop.
if (stop_thread_)
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
// We can return immediately if there's no work to do and the reactor is
// not supposed to block.
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
&& except_op_queue_.empty() && all_timer_queues_are_empty())
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
// Determine how long to block while waiting for events.
timespec timeout_buf = { 0, 0 };
timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
wait_in_progress_ = true;
lock.unlock();
// Block on the kqueue descriptor.
struct kevent events[128];
int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
lock.lock();
wait_in_progress_ = false;
// Block signals while dispatching operations.
asio::detail::signal_blocker sb;
// Dispatch the waiting events.
for (int i = 0; i < num_events; ++i)
{
int descriptor = events[i].ident;
if (descriptor == interrupter_.read_descriptor())
{
interrupter_.reset();
}
else if (events[i].filter == EVFILT_READ)
{
// Dispatch operations associated with the descriptor.
bool more_reads = false;
bool more_except = false;
if (events[i].flags & EV_ERROR)
{
int error = events[i].data;
except_op_queue_.dispatch_all_operations(descriptor, error);
read_op_queue_.dispatch_all_operations(descriptor, error);
}
else if (events[i].flags & EV_OOBAND)
{
more_except = except_op_queue_.dispatch_operation(descriptor, 0);
if (events[i].data > 0)
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
else
more_reads = read_op_queue_.has_operation(descriptor);
}
else
{
more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
more_except = except_op_queue_.has_operation(descriptor);
}
// Update the descriptor in the kqueue.
struct kevent event;
if (more_reads)
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
else if (more_except)
EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
else
EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
except_op_queue_.dispatch_all_operations(descriptor, error);
read_op_queue_.dispatch_all_operations(descriptor, error);
}
}
else if (events[i].filter == EVFILT_WRITE)
{
// Dispatch operations associated with the descriptor.
bool more_writes = false;
if (events[i].flags & EV_ERROR)
{
int error = events[i].data;
write_op_queue_.dispatch_all_operations(descriptor, error);
}
else
{
more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
}
// Update the descriptor in the kqueue.
struct kevent event;
if (more_writes)
EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
else
EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
{
int error = errno;
write_op_queue_.dispatch_all_operations(descriptor, error);
}
}
}
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
except_op_queue_.dispatch_cancellations();
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->dispatch_timers();
// Issue any pending cancellations.
for (std::size_t i = 0; i < pending_cancellations_.size(); ++i)
cancel_ops_unlocked(pending_cancellations_[i]);
pending_cancellations_.clear();
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
}
// Run the select loop in the thread.
void run_thread()
{
asio::detail::mutex::scoped_lock lock(mutex_);
while (!stop_thread_)
{
lock.unlock();
run(true);
lock.lock();
}
}
// Entry point for the select loop thread.
static void call_run_thread(kqueue_reactor* reactor)
{
reactor->run_thread();
}
// Interrupt the select loop.
void interrupt()
{
interrupter_.interrupt();
}
// Create the kqueue file descriptor. Throws an exception if the descriptor
// cannot be created.
static int do_kqueue_create()
{
int fd = kqueue();
if (fd == -1)
{
system_exception e("kqueue", errno);
boost::throw_exception(e);
}
return fd;
}
// Check if all timer queues are empty.
bool all_timer_queues_are_empty() const
{
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
if (!timer_queues_[i]->empty())
return false;
return true;
}
// Get the timeout value for the kevent call.
timespec* get_timeout(timespec& ts)
{
if (all_timer_queues_are_empty())
return 0;
// By default we will wait no longer than 5 minutes. This will ensure that
// any changes to the system clock are detected after no longer than this.
boost::posix_time::time_duration minimum_wait_duration
= boost::posix_time::minutes(5);
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
{
boost::posix_time::time_duration wait_duration
= timer_queues_[i]->wait_duration();
if (wait_duration < minimum_wait_duration)
minimum_wait_duration = wait_duration;
}
if (minimum_wait_duration > boost::posix_time::time_duration())
{
ts.tv_sec = minimum_wait_duration.total_seconds();
ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000;
}
else
{
ts.tv_sec = 0;
ts.tv_nsec = 0;
}
return &ts;
}
// Cancel all operations associated with the given descriptor. The do_cancel
// function of the handler objects will be invoked. This function does not
// acquire the epoll_reactor's mutex.
void cancel_ops_unlocked(socket_type descriptor)
{
bool interrupt = read_op_queue_.cancel_operations(descriptor);
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
if (interrupt)
interrupter_.interrupt();
}
// Mutex to protect access to internal data.
asio::detail::mutex mutex_;
// The epoll file descriptor.
int kqueue_fd_;
// Whether the kqueue wait call is currently in progress
bool wait_in_progress_;
// The interrupter is used to break a blocking epoll_wait call.
select_interrupter interrupter_;
// The queue of read operations.
reactor_op_queue<socket_type> read_op_queue_;
// The queue of write operations.
reactor_op_queue<socket_type> write_op_queue_;
// The queue of except operations.
reactor_op_queue<socket_type> except_op_queue_;
// The timer queues.
std::vector<timer_queue_base*> timer_queues_;
// The descriptors that are pending cancellation.
std::vector<socket_type> pending_cancellations_;
// Does the reactor loop thread need to stop.
bool stop_thread_;
// The thread that is running the reactor loop.
asio::detail::thread* thread_;
// Whether the service has been shut down.
bool shutdown_;
};
} // namespace detail
} // namespace asio
#endif // defined(ASIO_HAS_KQUEUE)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP

View File

@@ -0,0 +1,41 @@
//
// kqueue_reactor_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#if !defined(ASIO_DISABLE_KQUEUE)
#if defined(__MACH__) && defined(__APPLE__)
// Define this to indicate that epoll is supported on the target platform.
#define ASIO_HAS_KQUEUE 1
namespace asio {
namespace detail {
template <bool Own_Thread>
class kqueue_reactor;
} // namespace detail
} // namespace asio
#endif // defined(__MACH__) && defined(__APPLE__)
#endif // !defined(ASIO_DISABLE_KQUEUE)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP

View File

@@ -0,0 +1,50 @@
//
// mutex.hpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_MUTEX_HPP
#define ASIO_DETAIL_MUTEX_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
# include "asio/detail/null_mutex.hpp"
#elif defined(BOOST_WINDOWS)
# include "asio/detail/win_mutex.hpp"
#elif defined(BOOST_HAS_PTHREADS)
# include "asio/detail/posix_mutex.hpp"
#else
# error Only Windows and POSIX are supported!
#endif
namespace asio {
namespace detail {
#if !defined(BOOST_HAS_THREADS)
typedef null_mutex mutex;
#elif defined(BOOST_WINDOWS)
typedef win_mutex mutex;
#elif defined(BOOST_HAS_PTHREADS)
typedef posix_mutex mutex;
#endif
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_MUTEX_HPP

View File

@@ -0,0 +1,55 @@
//
// noncopyable.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NONCOPYABLE_HPP
#define ASIO_DETAIL_NONCOPYABLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include <boost/noncopyable.hpp>
#include <boost/detail/workaround.hpp>
#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
// Redefine the noncopyable class for Borland C++ since that compiler does not
// apply the empty base optimisation unless the base class contains a dummy
// char data member.
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:
noncopyable(const noncopyable&);
const noncopyable& operator=(const noncopyable&);
char dummy_;
};
#else
using boost::noncopyable;
#endif
} // namespace detail
using asio::detail::noncopyable;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NONCOPYABLE_HPP

View File

@@ -0,0 +1,68 @@
//
// null_event.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NULL_EVENT_HPP
#define ASIO_DETAIL_NULL_EVENT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
class null_event
: private noncopyable
{
public:
// Constructor.
null_event()
{
}
// Destructor.
~null_event()
{
}
// Signal the event.
void signal()
{
}
// Reset the event.
void clear()
{
}
// Wait for the event to become signalled.
void wait()
{
}
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_HAS_THREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NULL_EVENT_HPP

View File

@@ -0,0 +1,66 @@
//
// null_mutex.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NULL_MUTEX_HPP
#define ASIO_DETAIL_NULL_MUTEX_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/scoped_lock.hpp"
namespace asio {
namespace detail {
class null_mutex
: private noncopyable
{
public:
typedef asio::detail::scoped_lock<null_mutex> scoped_lock;
// Constructor.
null_mutex()
{
}
// Destructor.
~null_mutex()
{
}
// Lock the mutex.
void lock()
{
}
// Unlock the mutex.
void unlock()
{
}
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_HAS_THREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NULL_MUTEX_HPP

View File

@@ -0,0 +1,63 @@
//
// null_signal_blocker.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
class null_signal_blocker
: private noncopyable
{
public:
// Constructor blocks all signals for the calling thread.
null_signal_blocker()
{
}
// Destructor restores the previous signal mask.
~null_signal_blocker()
{
}
// Block all signals for the calling thread.
void block()
{
}
// Restore the previous signal mask.
void unblock()
{
}
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_HAS_THREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP

View File

@@ -0,0 +1,67 @@
//
// null_thread.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NULL_THREAD_HPP
#define ASIO_DETAIL_NULL_THREAD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
#include "asio/detail/push_options.hpp"
#include <boost/throw_exception.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/error.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
class null_thread
: private noncopyable
{
public:
// Constructor.
template <typename Function>
null_thread(Function f)
{
system_exception e("thread", asio::error::not_supported);
boost::throw_exception(e);
}
// Destructor.
~null_thread()
{
}
// Wait for the thread to exit.
void join()
{
}
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_HAS_THREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NULL_THREAD_HPP

View File

@@ -0,0 +1,70 @@
//
// null_tss_ptr.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP
#define ASIO_DETAIL_NULL_TSS_PTR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_HAS_THREADS)
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
template <typename T>
class null_tss_ptr
: private noncopyable
{
public:
// Constructor.
null_tss_ptr()
: value_(0)
{
}
// Destructor.
~null_tss_ptr()
{
}
// Get the value.
operator T*() const
{
return value_;
}
// Set the value.
void operator=(T* value)
{
value_ = value;
}
private:
T* value_;
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_HAS_THREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP

View File

@@ -0,0 +1,312 @@
//
// old_win_sdk_compat.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
// Guess whether we are building against on old Platform SDK.
#if !defined(IPPROTO_IPV6)
#define ASIO_HAS_OLD_WIN_SDK 1
#endif // !defined(IPPROTO_IPV6)
#if defined(ASIO_HAS_OLD_WIN_SDK)
// Emulation of types that are missing from old Platform SDKs.
namespace asio {
namespace detail {
enum
{
sockaddr_storage_maxsize = 128, // Maximum size.
sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment.
sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)),
sockaddr_storage_pad2size = (sockaddr_storage_maxsize -
(sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize))
};
struct sockaddr_storage_emulation
{
short ss_family;
char __ss_pad1[sockaddr_storage_pad1size];
__int64 __ss_align;
char __ss_pad2[sockaddr_storage_pad2size];
};
struct in6_addr_emulation
{
u_char s6_addr[16];
};
struct sockaddr_in6_emulation
{
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
in6_addr_emulation sin6_addr;
u_long sin6_scope_id;
};
struct ipv6_mreq_emulation
{
in6_addr_emulation ipv6mr_multiaddr;
unsigned int ipv6mr_interface;
};
#if !defined(IN6ADDR_ANY_INIT)
# define IN6ADDR_ANY_INIT { 0 }
#endif
#if !defined(IN6ADDR_LOOPBACK_INIT)
# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
#endif
struct addrinfo_emulation
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char* ai_canonname;
sockaddr* ai_addr;
addrinfo_emulation* ai_next;
};
#if !defined(AI_PASSIVE)
# define AI_PASSIVE 0x1
#endif
#if !defined(AI_CANONNAME)
# define AI_CANONNAME 0x2
#endif
#if !defined(AI_NUMERICHOST)
# define AI_NUMERICHOST 0x4
#endif
#if !defined(EAI_AGAIN)
# define EAI_AGAIN WSATRY_AGAIN
#endif
#if !defined(EAI_BADFLAGS)
# define EAI_BADFLAGS WSAEINVAL
#endif
#if !defined(EAI_FAIL)
# define EAI_FAIL WSANO_RECOVERY
#endif
#if !defined(EAI_FAMILY)
# define EAI_FAMILY WSAEAFNOSUPPORT
#endif
#if !defined(EAI_MEMORY)
# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY
#endif
#if !defined(EAI_NODATA)
# define EAI_NODATA WSANO_DATA
#endif
#if !defined(EAI_NONAME)
# define EAI_NONAME WSAHOST_NOT_FOUND
#endif
#if !defined(EAI_SERVICE)
# define EAI_SERVICE WSATYPE_NOT_FOUND
#endif
#if !defined(EAI_SOCKTYPE)
# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT
#endif
#if !defined(NI_NOFQDN)
# define NI_NOFQDN 0x01
#endif
#if !defined(NI_NUMERICHOST)
# define NI_NUMERICHOST 0x02
#endif
#if !defined(NI_NAMEREQD)
# define NI_NAMEREQD 0x04
#endif
#if !defined(NI_NUMERICSERV)
# define NI_NUMERICSERV 0x08
#endif
#if !defined(NI_DGRAM)
# define NI_DGRAM 0x10
#endif
#if !defined(IPPROTO_IPV6)
# define IPPROTO_IPV6 41
#endif
#if !defined(IPV6_MULTICAST_IF)
# define IPV6_MULTICAST_IF 9
#endif
#if !defined(IPV6_MULTICAST_HOPS)
# define IPV6_MULTICAST_HOPS 10
#endif
#if !defined(IPV6_MULTICAST_LOOP)
# define IPV6_MULTICAST_LOOP 11
#endif
#if !defined(IPV6_JOIN_GROUP)
# define IPV6_JOIN_GROUP 12
#endif
#if !defined(IPV6_LEAVE_GROUP)
# define IPV6_LEAVE_GROUP 13
#endif
inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0)
&& (a->s6_addr[1] == 0)
&& (a->s6_addr[2] == 0)
&& (a->s6_addr[3] == 0)
&& (a->s6_addr[4] == 0)
&& (a->s6_addr[5] == 0)
&& (a->s6_addr[6] == 0)
&& (a->s6_addr[7] == 0)
&& (a->s6_addr[8] == 0)
&& (a->s6_addr[9] == 0)
&& (a->s6_addr[10] == 0)
&& (a->s6_addr[11] == 0)
&& (a->s6_addr[12] == 0)
&& (a->s6_addr[13] == 0)
&& (a->s6_addr[14] == 0)
&& (a->s6_addr[15] == 0));
}
inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0)
&& (a->s6_addr[1] == 0)
&& (a->s6_addr[2] == 0)
&& (a->s6_addr[3] == 0)
&& (a->s6_addr[4] == 0)
&& (a->s6_addr[5] == 0)
&& (a->s6_addr[6] == 0)
&& (a->s6_addr[7] == 0)
&& (a->s6_addr[8] == 0)
&& (a->s6_addr[9] == 0)
&& (a->s6_addr[10] == 0)
&& (a->s6_addr[11] == 0)
&& (a->s6_addr[12] == 0)
&& (a->s6_addr[13] == 0)
&& (a->s6_addr[14] == 0)
&& (a->s6_addr[15] == 1));
}
inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a)
{
return (a->s6_addr[0] == 0xff);
}
inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80));
}
inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0));
}
inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0)
&& (a->s6_addr[1] == 0)
&& (a->s6_addr[2] == 0)
&& (a->s6_addr[3] == 0)
&& (a->s6_addr[4] == 0)
&& (a->s6_addr[5] == 0)
&& (a->s6_addr[6] == 0)
&& (a->s6_addr[7] == 0)
&& (a->s6_addr[8] == 0)
&& (a->s6_addr[9] == 0)
&& (a->s6_addr[10] == 0xff)
&& (a->s6_addr[11] == 0xff));
}
inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a)
{
return ((a->s6_addr[0] == 0)
&& (a->s6_addr[1] == 0)
&& (a->s6_addr[2] == 0)
&& (a->s6_addr[3] == 0)
&& (a->s6_addr[4] == 0)
&& (a->s6_addr[5] == 0)
&& (a->s6_addr[6] == 0)
&& (a->s6_addr[7] == 0)
&& (a->s6_addr[8] == 0)
&& (a->s6_addr[9] == 0)
&& (a->s6_addr[10] == 0xff)
&& (a->s6_addr[11] == 0xff)
&& !((a->s6_addr[12] == 0)
&& (a->s6_addr[13] == 0)
&& (a->s6_addr[14] == 0)
&& ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1))));
}
inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a)
{
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1);
}
inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a)
{
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2);
}
inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a)
{
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5);
}
inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a)
{
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8);
}
inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a)
{
return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe);
}
} // namespace detail
} // namespace asio
#endif // defined(ASIO_HAS_OLD_WIN_SDK)
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP

View File

@@ -0,0 +1,104 @@
//
// pipe_select_interrupter.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
#include "asio/detail/push_options.hpp"
#include <fcntl.h>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/socket_types.hpp"
namespace asio {
namespace detail {
class pipe_select_interrupter
{
public:
// Constructor.
pipe_select_interrupter()
{
int pipe_fds[2];
if (pipe(pipe_fds) == 0)
{
read_descriptor_ = pipe_fds[0];
::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
write_descriptor_ = pipe_fds[1];
::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
}
}
// Destructor.
~pipe_select_interrupter()
{
if (read_descriptor_ != -1)
::close(read_descriptor_);
if (write_descriptor_ != -1)
::close(write_descriptor_);
}
// Interrupt the select call.
void interrupt()
{
char byte = 0;
::write(write_descriptor_, &byte, 1);
}
// Reset the select interrupt. Returns true if the call was interrupted.
bool reset()
{
char data[1024];
int bytes_read = ::read(read_descriptor_, data, sizeof(data));
bool was_interrupted = (bytes_read > 0);
while (bytes_read == sizeof(data))
bytes_read = ::read(read_descriptor_, data, sizeof(data));
return was_interrupted;
}
// Get the read descriptor to be passed to select.
int read_descriptor() const
{
return read_descriptor_;
}
private:
// The read end of a connection used to interrupt the select call. This file
// descriptor is passed to select such that when it is time to stop, a single
// byte will be written on the other end of the connection and this
// descriptor will become readable.
int read_descriptor_;
// The write end of a connection used to interrupt the select call. A single
// byte may be written to this to wake up the select which is waiting for the
// other end to become readable.
int write_descriptor_;
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP

View File

@@ -0,0 +1,88 @@
//
// pop_options.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// No header guard
#if defined(__COMO__)
// Comeau C++
#elif defined(__DMC__)
// Digital Mars C++
#elif defined(__INTEL_COMPILER) || defined(__ICL) \
|| defined(__ICC) || defined(__ECC)
// Intel C++
#elif defined(__GNUC__)
// GNU C++
# if defined(__MINGW32__) || defined(__CYGWIN__)
# pragma pack (pop)
# endif
#elif defined(__KCC)
// Kai C++
#elif defined(__sgi)
// SGI MIPSpro C++
#elif defined(__DECCXX)
// Compaq Tru64 Unix cxx
#elif defined(__ghs)
// Greenhills C++
#elif defined(__BORLANDC__)
// Borland C++
# pragma option pop
# pragma nopushoptwarn
# pragma nopackwarning
#elif defined(__MWERKS__)
// Metrowerks CodeWarrior
#elif defined(__SUNPRO_CC)
// Sun Workshop Compiler C++
#elif defined(__HP_aCC)
// HP aCC
#elif defined(__MRC__) || defined(__SC__)
// MPW MrCpp or SCpp
#elif defined(__IBMCPP__)
// IBM Visual Age
#elif defined(_MSC_VER)
// Microsoft Visual C++
//
// Must remain the last #elif since some other vendors (Metrowerks, for example)
// also #define _MSC_VER
# pragma warning (pop)
# pragma pack (pop)
#endif

View File

@@ -0,0 +1,107 @@
//
// posix_event.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_EVENT_HPP
#define ASIO_DETAIL_POSIX_EVENT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_HAS_PTHREADS)
#include "asio/detail/push_options.hpp"
#include <boost/throw_exception.hpp>
#include <pthread.h>
#include "asio/detail/pop_options.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
class posix_event
: private noncopyable
{
public:
// Constructor.
posix_event()
: signalled_(false)
{
int error = ::pthread_mutex_init(&mutex_, 0);
if (error != 0)
{
system_exception e("event", error);
boost::throw_exception(e);
}
error = ::pthread_cond_init(&cond_, 0);
if (error != 0)
{
::pthread_mutex_destroy(&mutex_);
system_exception e("event", error);
boost::throw_exception(e);
}
}
// Destructor.
~posix_event()
{
::pthread_cond_destroy(&cond_);
::pthread_mutex_destroy(&mutex_);
}
// Signal the event.
void signal()
{
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
signalled_ = true;
::pthread_cond_signal(&cond_); // Ignore EINVAL.
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
}
// Reset the event.
void clear()
{
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
signalled_ = false;
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
}
// Wait for the event to become signalled.
void wait()
{
::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
while (!signalled_)
::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL.
::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
}
private:
::pthread_mutex_t mutex_;
::pthread_cond_t cond_;
bool signalled_;
};
} // namespace detail
} // namespace asio
#endif // defined(BOOST_HAS_PTHREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_EVENT_HPP

View File

@@ -0,0 +1,71 @@
//
// posix_fd_set_adapter.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/socket_types.hpp"
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
namespace asio {
namespace detail {
// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements.
class posix_fd_set_adapter
{
public:
posix_fd_set_adapter()
: max_descriptor_(invalid_socket)
{
FD_ZERO(&fd_set_);
}
void set(socket_type descriptor)
{
if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_)
max_descriptor_ = descriptor;
FD_SET(descriptor, &fd_set_);
}
bool is_set(socket_type descriptor) const
{
return FD_ISSET(descriptor, &fd_set_) != 0;
}
operator fd_set*()
{
return &fd_set_;
}
socket_type max_descriptor() const
{
return max_descriptor_;
}
private:
fd_set fd_set_;
socket_type max_descriptor_;
};
} // namespace detail
} // namespace asio
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP

View File

@@ -0,0 +1,94 @@
//
// posix_mutex.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP
#define ASIO_DETAIL_POSIX_MUTEX_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_HAS_PTHREADS)
#include "asio/detail/push_options.hpp"
#include <boost/throw_exception.hpp>
#include <pthread.h>
#include "asio/detail/pop_options.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/scoped_lock.hpp"
namespace asio {
namespace detail {
class posix_mutex
: private noncopyable
{
public:
typedef asio::detail::scoped_lock<posix_mutex> scoped_lock;
// Constructor.
posix_mutex()
{
int error = ::pthread_mutex_init(&mutex_, 0);
if (error != 0)
{
system_exception e("mutex", error);
boost::throw_exception(e);
}
}
// Destructor.
~posix_mutex()
{
::pthread_mutex_destroy(&mutex_);
}
// Lock the mutex.
void lock()
{
int error = ::pthread_mutex_lock(&mutex_);
if (error != 0)
{
system_exception e("mutex", error);
boost::throw_exception(e);
}
}
// Unlock the mutex.
void unlock()
{
int error = ::pthread_mutex_unlock(&mutex_);
if (error != 0)
{
system_exception e("mutex", error);
boost::throw_exception(e);
}
}
private:
::pthread_mutex_t mutex_;
};
} // namespace detail
} // namespace asio
#endif // defined(BOOST_HAS_PTHREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_MUTEX_HPP

View File

@@ -0,0 +1,89 @@
//
// posix_signal_blocker.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_HAS_PTHREADS)
#include "asio/detail/push_options.hpp"
#include <csignal>
#include <pthread.h>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
class posix_signal_blocker
: private noncopyable
{
public:
// Constructor blocks all signals for the calling thread.
posix_signal_blocker()
: blocked_(false)
{
sigset_t new_mask;
sigfillset(&new_mask);
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
}
// Destructor restores the previous signal mask.
~posix_signal_blocker()
{
if (blocked_)
pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
}
// Block all signals for the calling thread.
void block()
{
if (!blocked_)
{
sigset_t new_mask;
sigfillset(&new_mask);
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
}
}
// Restore the previous signal mask.
void unblock()
{
if (blocked_)
blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0);
}
private:
// Have signals been blocked.
bool blocked_;
// The previous signal mask.
sigset_t old_mask_;
};
} // namespace detail
} // namespace asio
#endif // defined(BOOST_HAS_PTHREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP

View File

@@ -0,0 +1,125 @@
//
// posix_thread.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_THREAD_HPP
#define ASIO_DETAIL_POSIX_THREAD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_HAS_PTHREADS)
#include "asio/detail/push_options.hpp"
#include <memory>
#include <boost/throw_exception.hpp>
#include <pthread.h>
#include "asio/detail/pop_options.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
extern "C" void* asio_detail_posix_thread_function(void* arg);
class posix_thread
: private noncopyable
{
public:
// Constructor.
template <typename Function>
posix_thread(Function f)
: joined_(false)
{
std::auto_ptr<func_base> arg(new func<Function>(f));
int error = ::pthread_create(&thread_, 0,
asio_detail_posix_thread_function, arg.get());
if (error != 0)
{
system_exception e("thread", error);
boost::throw_exception(e);
}
arg.release();
}
// Destructor.
~posix_thread()
{
if (!joined_)
::pthread_detach(thread_);
}
// Wait for the thread to exit.
void join()
{
if (!joined_)
{
::pthread_join(thread_, 0);
joined_ = true;
}
}
private:
friend void* asio_detail_posix_thread_function(void* arg);
class func_base
{
public:
virtual ~func_base() {}
virtual void run() = 0;
};
template <typename Function>
class func
: public func_base
{
public:
func(Function f)
: f_(f)
{
}
virtual void run()
{
f_();
}
private:
Function f_;
};
::pthread_t thread_;
bool joined_;
};
inline void* asio_detail_posix_thread_function(void* arg)
{
std::auto_ptr<posix_thread::func_base> f(
static_cast<posix_thread::func_base*>(arg));
f->run();
return 0;
}
} // namespace detail
} // namespace asio
#endif // defined(BOOST_HAS_PTHREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_THREAD_HPP

View File

@@ -0,0 +1,84 @@
//
// posix_tss_ptr.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP
#define ASIO_DETAIL_POSIX_TSS_PTR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#if defined(BOOST_HAS_PTHREADS)
#include "asio/detail/push_options.hpp"
#include <boost/throw_exception.hpp>
#include <pthread.h>
#include "asio/detail/pop_options.hpp"
#include "asio/system_exception.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
template <typename T>
class posix_tss_ptr
: private noncopyable
{
public:
// Constructor.
posix_tss_ptr()
{
int error = ::pthread_key_create(&tss_key_, 0);
if (error != 0)
{
system_exception e("tss", error);
boost::throw_exception(e);
}
}
// Destructor.
~posix_tss_ptr()
{
::pthread_key_delete(tss_key_);
}
// Get the value.
operator T*() const
{
return static_cast<T*>(::pthread_getspecific(tss_key_));
}
// Set the value.
void operator=(T* value)
{
::pthread_setspecific(tss_key_, value);
}
private:
// Thread-specific storage to allow unlocked access to determine whether a
// thread is a member of the pool.
pthread_key_t tss_key_;
};
} // namespace detail
} // namespace asio
#endif // defined(BOOST_HAS_PTHREADS)
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP

View File

@@ -0,0 +1,106 @@
//
// push_options.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// No header guard
#if defined(__COMO__)
// Comeau C++
#elif defined(__DMC__)
// Digital Mars C++
#elif defined(__INTEL_COMPILER) || defined(__ICL) \
|| defined(__ICC) || defined(__ECC)
// Intel C++
#elif defined(__GNUC__)
// GNU C++
# if defined(__MINGW32__) || defined(__CYGWIN__)
# pragma pack (push, 8)
# endif
#elif defined(__KCC)
// Kai C++
#elif defined(__sgi)
// SGI MIPSpro C++
#elif defined(__DECCXX)
// Compaq Tru64 Unix cxx
#elif defined(__ghs)
// Greenhills C++
#elif defined(__BORLANDC__)
// Borland C++
# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi-
# pragma nopushoptwarn
# pragma nopackwarning
# if !defined(__MT__)
# error Multithreaded RTL must be selected.
# endif // !defined(__MT__)
#elif defined(__MWERKS__)
// Metrowerks CodeWarrior
#elif defined(__SUNPRO_CC)
// Sun Workshop Compiler C++
#elif defined(__HP_aCC)
// HP aCC
#elif defined(__MRC__) || defined(__SC__)
// MPW MrCpp or SCpp
#elif defined(__IBMCPP__)
// IBM Visual Age
#elif defined(_MSC_VER)
// Microsoft Visual C++
//
// Must remain the last #elif since some other vendors (Metrowerks, for example)
// also #define _MSC_VER
# pragma warning (disable:4103)
# pragma warning (push)
# pragma warning (disable:4244)
# pragma warning (disable:4355)
# pragma warning (disable:4675)
# pragma pack (push, 8)
// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler
// has a tendency to incorrectly optimise away some calls to member template
// functions, even though those functions contain code that should not be
// optimised away! Therefore we will always disable this optimisation option
// for the MSVC6 compiler.
# if (_MSC_VER < 1300)
# pragma optimize ("g", off)
# endif
# if !defined(_MT)
# error Multithreaded RTL must be selected.
# endif // !defined(_MT)
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,379 @@
//
// reactor_op_queue.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <memory>
#include "asio/detail/pop_options.hpp"
#include "asio/error.hpp"
#include "asio/detail/hash_map.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
template <typename Descriptor>
class reactor_op_queue
: private noncopyable
{
public:
// Constructor.
reactor_op_queue()
: operations_(),
cancelled_operations_(0),
cleanup_operations_(0)
{
}
// Add a new operation to the queue. Returns true if this is the only
// operation for the given descriptor, in which case the reactor's event
// demultiplexing function call may need to be interrupted and restarted.
template <typename Handler>
bool enqueue_operation(Descriptor descriptor, Handler handler)
{
op_base* new_op = new op<Handler>(descriptor, handler);
typedef typename operation_map::iterator iterator;
typedef typename operation_map::value_type value_type;
std::pair<iterator, bool> entry =
operations_.insert(value_type(descriptor, new_op));
if (entry.second)
return true;
op_base* current_op = entry.first->second;
while (current_op->next_)
current_op = current_op->next_;
current_op->next_ = new_op;
return false;
}
// Cancel all operations associated with the descriptor. Any operations
// pending for the descriptor will be notified that they have been cancelled
// next time dispatch_cancellations is called. Returns true if any operations
// were cancelled, in which case the reactor's event demultiplexing function
// may need to be interrupted and restarted.
bool cancel_operations(Descriptor descriptor)
{
typename operation_map::iterator i = operations_.find(descriptor);
if (i != operations_.end())
{
op_base* last_op = i->second;
while (last_op->next_)
last_op = last_op->next_;
last_op->next_ = cancelled_operations_;
cancelled_operations_ = i->second;
operations_.erase(i);
return true;
}
return false;
}
// Whether there are no operations in the queue.
bool empty() const
{
return operations_.empty();
}
// Determine whether there are any operations associated with the descriptor.
bool has_operation(Descriptor descriptor) const
{
return operations_.find(descriptor) != operations_.end();
}
// Dispatch the first operation corresponding to the descriptor. Returns true
// if there are more operations queued for the descriptor.
bool dispatch_operation(Descriptor descriptor, int result)
{
typename operation_map::iterator i = operations_.find(descriptor);
if (i != operations_.end())
{
op_base* this_op = i->second;
i->second = this_op->next_;
this_op->next_ = cleanup_operations_;
cleanup_operations_ = this_op;
bool done = this_op->invoke(result);
if (done)
{
// Operation has finished.
if (i->second)
{
return true;
}
else
{
operations_.erase(i);
return false;
}
}
else
{
// Operation wants to be called again. Leave it at the front of the
// queue for this descriptor, and remove from the cleanup list.
cleanup_operations_ = this_op->next_;
this_op->next_ = i->second;
i->second = this_op;
return true;
}
}
return false;
}
// Dispatch all operations corresponding to the descriptor.
void dispatch_all_operations(Descriptor descriptor, int result)
{
typename operation_map::iterator i = operations_.find(descriptor);
if (i != operations_.end())
{
while (i->second)
{
op_base* this_op = i->second;
i->second = this_op->next_;
this_op->next_ = cleanup_operations_;
cleanup_operations_ = this_op;
bool done = this_op->invoke(result);
if (!done)
{
// Operation has not finished yet, so leave at front of queue, and
// remove from the cleanup list.
cleanup_operations_ = this_op->next_;
this_op->next_ = i->second;
i->second = this_op;
return;
}
operations_.erase(i);
}
}
}
// Fill a descriptor set with the descriptors corresponding to each active
// operation.
template <typename Descriptor_Set>
void get_descriptors(Descriptor_Set& descriptors)
{
typename operation_map::iterator i = operations_.begin();
while (i != operations_.end())
{
descriptors.set(i->first);
++i;
}
}
// Dispatch the operations corresponding to the ready file descriptors
// contained in the given descriptor set.
template <typename Descriptor_Set>
void dispatch_descriptors(const Descriptor_Set& descriptors, int result)
{
typename operation_map::iterator i = operations_.begin();
while (i != operations_.end())
{
typename operation_map::iterator op_iter = i++;
if (descriptors.is_set(op_iter->first))
{
op_base* this_op = op_iter->second;
op_iter->second = this_op->next_;
this_op->next_ = cleanup_operations_;
cleanup_operations_ = this_op;
bool done = this_op->invoke(result);
if (done)
{
if (!op_iter->second)
operations_.erase(op_iter);
}
else
{
// Operation has not finished yet, so leave at front of queue, and
// remove from the cleanup list.
cleanup_operations_ = this_op->next_;
this_op->next_ = op_iter->second;
op_iter->second = this_op;
}
}
}
}
// Dispatch any pending cancels for operations.
void dispatch_cancellations()
{
while (cancelled_operations_)
{
op_base* this_op = cancelled_operations_;
cancelled_operations_ = this_op->next_;
this_op->next_ = cleanup_operations_;
cleanup_operations_ = this_op;
this_op->invoke(asio::error::operation_aborted);
}
}
// Destroy operations that are waiting to be cleaned up.
void cleanup_operations()
{
while (cleanup_operations_)
{
op_base* next_op = cleanup_operations_->next_;
cleanup_operations_->next_ = 0;
cleanup_operations_->destroy();
cleanup_operations_ = next_op;
}
}
// Destroy all operations owned by the queue.
void destroy_operations()
{
while (cancelled_operations_)
{
op_base* next_op = cancelled_operations_->next_;
cancelled_operations_->next_ = 0;
cancelled_operations_->destroy();
cancelled_operations_ = next_op;
}
while (cleanup_operations_)
{
op_base* next_op = cleanup_operations_->next_;
cleanup_operations_->next_ = 0;
cleanup_operations_->destroy();
cleanup_operations_ = next_op;
}
typename operation_map::iterator i = operations_.begin();
while (i != operations_.end())
{
typename operation_map::iterator op_iter = i++;
op_base* curr_op = op_iter->second;
operations_.erase(op_iter);
while (curr_op)
{
op_base* next_op = curr_op->next_;
curr_op->next_ = 0;
curr_op->destroy();
curr_op = next_op;
}
}
}
private:
// Base class for reactor operations. A function pointer is used instead of
// virtual functions to avoid the associated overhead.
class op_base
{
public:
// Get the descriptor associated with the operation.
Descriptor descriptor() const
{
return descriptor_;
}
// Perform the operation.
bool invoke(int result)
{
return invoke_func_(this, result);
}
// Destroy the operation.
void destroy()
{
return destroy_func_(this);
}
protected:
typedef bool (*invoke_func_type)(op_base*, int);
typedef void (*destroy_func_type)(op_base*);
// Construct an operation for the given descriptor.
op_base(invoke_func_type invoke_func,
destroy_func_type destroy_func, Descriptor descriptor)
: invoke_func_(invoke_func),
destroy_func_(destroy_func),
descriptor_(descriptor),
next_(0)
{
}
// Prevent deletion through this type.
~op_base()
{
}
private:
friend class reactor_op_queue<Descriptor>;
// The function to be called to dispatch the handler.
invoke_func_type invoke_func_;
// The function to be called to delete the handler.
destroy_func_type destroy_func_;
// The descriptor associated with the operation.
Descriptor descriptor_;
// The next operation for the same file descriptor.
op_base* next_;
};
// Adaptor class template for using handlers in operations.
template <typename Handler>
class op
: public op_base
{
public:
// Constructor.
op(Descriptor descriptor, Handler handler)
: op_base(&op<Handler>::invoke_handler,
&op<Handler>::destroy_handler, descriptor),
handler_(handler)
{
}
// Invoke the handler.
static bool invoke_handler(op_base* base, int result)
{
return static_cast<op<Handler>*>(base)->handler_(result);
}
// Delete the handler.
static void destroy_handler(op_base* base)
{
delete static_cast<op<Handler>*>(base);
}
private:
Handler handler_;
};
// The type for a map of operations.
typedef hash_map<Descriptor, op_base*> operation_map;
// The operations that are currently executing asynchronously.
operation_map operations_;
// The list of operations that have been cancelled.
op_base* cancelled_operations_;
// The list of operations to be destroyed.
op_base* cleanup_operations_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP

View File

@@ -0,0 +1,361 @@
//
// resolver_service.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP
#define ASIO_DETAIL_RESOLVER_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <cstring>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/thread.hpp"
namespace asio {
namespace detail {
template <typename Protocol>
class resolver_service
: public asio::io_service::service
{
private:
// Helper class to perform exception-safe cleanup of addrinfo objects.
class auto_addrinfo
: private asio::detail::noncopyable
{
public:
explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
: ai_(ai)
{
}
~auto_addrinfo()
{
if (ai_)
socket_ops::freeaddrinfo(ai_);
}
operator asio::detail::addrinfo_type*()
{
return ai_;
}
private:
asio::detail::addrinfo_type* ai_;
};
public:
// The implementation type of the resolver. The shared pointer is used as a
// cancellation token to indicate to the background thread that the operation
// has been cancelled.
typedef boost::shared_ptr<void> implementation_type;
struct noop_deleter { void operator()(void*) {} };
// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
// The query type.
typedef typename Protocol::resolver_query query_type;
// The iterator type.
typedef typename Protocol::resolver_iterator iterator_type;
// Constructor.
resolver_service(asio::io_service& io_service)
: asio::io_service::service(io_service),
mutex_(),
work_io_service_(new asio::io_service),
work_(new asio::io_service::work(*work_io_service_)),
work_thread_(0)
{
}
// Destructor.
~resolver_service()
{
shutdown_service();
}
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
work_.reset();
if (work_io_service_)
{
work_io_service_->interrupt();
if (work_thread_)
{
work_thread_->join();
work_thread_.reset();
}
work_io_service_.reset();
}
}
// Construct a new resolver implementation.
void construct(implementation_type& impl)
{
impl.reset(static_cast<void*>(0), noop_deleter());
}
// Destroy a resolver implementation.
void destroy(implementation_type&)
{
}
// Cancel pending asynchronous operations.
void cancel(implementation_type& impl)
{
impl.reset(static_cast<void*>(0), noop_deleter());
}
// Resolve a query to a list of entries.
template <typename Error_Handler>
iterator_type resolve(implementation_type&, const query_type& query,
Error_Handler error_handler)
{
asio::detail::addrinfo_type* address_info = 0;
std::string host_name = query.host_name();
std::string service_name = query.service_name();
asio::detail::addrinfo_type hints = query.hints();
int result = socket_ops::getaddrinfo(
host_name.length() ? host_name.c_str() : 0,
service_name.c_str(), &hints, &address_info);
auto_addrinfo auto_address_info(address_info);
error_handler(asio::error(result));
if (result != 0)
return iterator_type();
return iterator_type::create(address_info, host_name, service_name);
}
template <typename Handler>
class resolve_query_handler
{
public:
resolve_query_handler(implementation_type impl, const query_type& query,
asio::io_service& io_service, Handler handler)
: impl_(impl),
query_(query),
io_service_(io_service),
work_(io_service),
handler_(handler)
{
}
void operator()()
{
// Check if the operation has been cancelled.
if (impl_.expired())
{
iterator_type iterator;
io_service_.post(asio::detail::bind_handler(handler_,
asio::error(asio::error::operation_aborted),
iterator));
return;
}
// Perform the blocking host resolution operation.
asio::detail::addrinfo_type* address_info = 0;
std::string host_name = query_.host_name();
std::string service_name = query_.service_name();
asio::detail::addrinfo_type hints = query_.hints();
int result = socket_ops::getaddrinfo(
host_name.length() ? host_name.c_str() : 0,
service_name.c_str(), &hints, &address_info);
auto_addrinfo auto_address_info(address_info);
// Invoke the handler and pass the result.
asio::error e(result);
iterator_type iterator;
if (result == 0)
iterator = iterator_type::create(address_info, host_name, service_name);
io_service_.post(asio::detail::bind_handler(
handler_, e, iterator));
}
private:
boost::weak_ptr<void> impl_;
query_type query_;
asio::io_service& io_service_;
asio::io_service::work work_;
Handler handler_;
};
// Asynchronously resolve a query to a list of entries.
template <typename Handler>
void async_resolve(implementation_type& impl, const query_type& query,
Handler handler)
{
if (work_io_service_)
{
start_work_thread();
work_io_service_->post(
resolve_query_handler<Handler>(
impl, query, io_service(), handler));
}
}
// Resolve an endpoint to a list of entries.
template <typename Error_Handler>
iterator_type resolve(implementation_type&,
const endpoint_type& endpoint, Error_Handler error_handler)
{
// First try resolving with the service name. If that fails try resolving
// but allow the service to be returned as a number.
char host_name[NI_MAXHOST];
char service_name[NI_MAXSERV];
int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
int result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
if (result)
{
flags |= NI_NUMERICSERV;
result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
}
error_handler(asio::error(result));
if (result != 0)
return iterator_type();
return iterator_type::create(endpoint, host_name, service_name);
}
template <typename Handler>
class resolve_endpoint_handler
{
public:
resolve_endpoint_handler(implementation_type impl,
const endpoint_type& endpoint, asio::io_service& io_service,
Handler handler)
: impl_(impl),
endpoint_(endpoint),
io_service_(io_service),
work_(io_service),
handler_(handler)
{
}
void operator()()
{
// Check if the operation has been cancelled.
if (impl_.expired())
{
iterator_type iterator;
io_service_.post(asio::detail::bind_handler(handler_,
asio::error(asio::error::operation_aborted),
iterator));
return;
}
// First try resolving with the service name. If that fails try resolving
// but allow the service to be returned as a number.
char host_name[NI_MAXHOST];
char service_name[NI_MAXSERV];
int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
int result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
if (result)
{
flags |= NI_NUMERICSERV;
result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
}
// Invoke the handler and pass the result.
asio::error e(result);
iterator_type iterator;
if (result == 0)
iterator = iterator_type::create(endpoint_, host_name, service_name);
io_service_.post(asio::detail::bind_handler(
handler_, e, iterator));
}
private:
boost::weak_ptr<void> impl_;
endpoint_type endpoint_;
asio::io_service& io_service_;
asio::io_service::work work_;
Handler handler_;
};
// Asynchronously resolve an endpoint to a list of entries.
template <typename Handler>
void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
Handler handler)
{
if (work_io_service_)
{
start_work_thread();
work_io_service_->post(
resolve_endpoint_handler<Handler>(
impl, endpoint, io_service(), handler));
}
}
private:
// Helper class to run the work io_service in a thread.
class work_io_service_runner
{
public:
work_io_service_runner(asio::io_service& io_service)
: io_service_(io_service) {}
void operator()() { io_service_.run(); }
private:
asio::io_service& io_service_;
};
// Start the work thread if it's not already running.
void start_work_thread()
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (work_thread_ == 0)
{
work_thread_.reset(new asio::detail::thread(
work_io_service_runner(*work_io_service_)));
}
}
// Mutex to protect access to internal data.
asio::detail::mutex mutex_;
// Private io_service used for performing asynchronous host resolution.
boost::scoped_ptr<asio::io_service> work_io_service_;
// Work for the private io_service to perform.
boost::scoped_ptr<asio::io_service::work> work_;
// Thread used for running the work io_service's run loop.
boost::scoped_ptr<asio::detail::thread> work_thread_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP

View File

@@ -0,0 +1,79 @@
//
// scoped_lock.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP
#define ASIO_DETAIL_SCOPED_LOCK_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/noncopyable.hpp"
namespace asio {
namespace detail {
// Helper class to lock and unlock a mutex automatically.
template <typename Mutex>
class scoped_lock
: private noncopyable
{
public:
// Constructor acquires the lock.
scoped_lock(Mutex& m)
: mutex_(m)
{
mutex_.lock();
locked_ = true;
}
// Destructor releases the lock.
~scoped_lock()
{
if (locked_)
mutex_.unlock();
}
// Explicitly acquire the lock.
void lock()
{
if (!locked_)
{
mutex_.lock();
locked_ = true;
}
}
// Explicitly release the lock.
void unlock()
{
if (locked_)
{
mutex_.unlock();
locked_ = false;
}
}
private:
// The underlying mutex.
Mutex& mutex_;
// Whether the mutex is currently locked or unlocked.
bool locked_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_SCOPED_LOCK_HPP

View File

@@ -0,0 +1,41 @@
//
// select_interrupter.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP
#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/config.hpp>
#include "asio/detail/pop_options.hpp"
#include "asio/detail/pipe_select_interrupter.hpp"
#include "asio/detail/socket_select_interrupter.hpp"
namespace asio {
namespace detail {
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
typedef socket_select_interrupter select_interrupter;
#else
typedef pipe_select_interrupter select_interrupter;
#endif
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP

View File

@@ -0,0 +1,435 @@
//
// select_reactor.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP
#define ASIO_DETAIL_SELECT_REACTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <vector>
#include "asio/detail/pop_options.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/fd_set_adapter.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/task_io_service.hpp"
#include "asio/detail/thread.hpp"
#include "asio/detail/reactor_op_queue.hpp"
#include "asio/detail/select_interrupter.hpp"
#include "asio/detail/select_reactor_fwd.hpp"
#include "asio/detail/signal_blocker.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_queue.hpp"
namespace asio {
namespace detail {
template <bool Own_Thread>
class select_reactor
: public asio::io_service::service
{
public:
// Constructor.
select_reactor(asio::io_service& io_service)
: asio::io_service::service(io_service),
mutex_(),
select_in_progress_(false),
interrupter_(),
read_op_queue_(),
write_op_queue_(),
except_op_queue_(),
pending_cancellations_(),
stop_thread_(false),
thread_(0),
shutdown_(false)
{
if (Own_Thread)
{
asio::detail::signal_blocker sb;
thread_ = new asio::detail::thread(
bind_handler(&select_reactor::call_run_thread, this));
}
}
// Destructor.
~select_reactor()
{
shutdown_service();
}
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
asio::detail::mutex::scoped_lock lock(mutex_);
shutdown_ = true;
stop_thread_ = true;
lock.unlock();
if (thread_)
{
interrupter_.interrupt();
thread_->join();
delete thread_;
thread_ = 0;
}
read_op_queue_.destroy_operations();
write_op_queue_.destroy_operations();
except_op_queue_.destroy_operations();
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->destroy_timers();
timer_queues_.clear();
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type descriptor)
{
return 0;
}
// Start a new read operation. The handler object will be invoked when the
// given descriptor is ready to be read, or an error has occurred.
template <typename Handler>
void start_read_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (read_op_queue_.enqueue_operation(descriptor, handler))
interrupter_.interrupt();
}
// Start a new write operation. The handler object will be invoked when the
// given descriptor is ready to be written, or an error has occurred.
template <typename Handler>
void start_write_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (write_op_queue_.enqueue_operation(descriptor, handler))
interrupter_.interrupt();
}
// Start a new exception operation. The handler object will be invoked when
// the given descriptor has exception information, or an error has occurred.
template <typename Handler>
void start_except_op(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (except_op_queue_.enqueue_operation(descriptor, handler))
interrupter_.interrupt();
}
// Start new write and exception operations. The handler object will be
// invoked when the given descriptor is ready for writing or has exception
// information available, or an error has occurred.
template <typename Handler>
void start_write_and_except_ops(socket_type descriptor, Handler handler)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
{
bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler);
interrupt = except_op_queue_.enqueue_operation(descriptor, handler)
|| interrupt;
if (interrupt)
interrupter_.interrupt();
}
}
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
void cancel_ops(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
cancel_ops_unlocked(descriptor);
}
// Enqueue cancellation of all operations associated with the given
// descriptor. The handlers associated with the descriptor will be invoked
// with the operation_aborted error. This function does not acquire the
// select_reactor's mutex, and so should only be used from within a reactor
// handler.
void enqueue_cancel_ops_unlocked(socket_type descriptor)
{
pending_cancellations_.push_back(descriptor);
}
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
void close_descriptor(socket_type descriptor)
{
asio::detail::mutex::scoped_lock lock(mutex_);
cancel_ops_unlocked(descriptor);
}
// Add a new timer queue to the reactor.
template <typename Time_Traits>
void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
{
asio::detail::mutex::scoped_lock lock(mutex_);
timer_queues_.push_back(&timer_queue);
}
// Schedule a timer in the given timer queue to expire at the specified
// absolute time. The handler object will be invoked when the timer expires.
template <typename Time_Traits, typename Handler>
void schedule_timer(timer_queue<Time_Traits>& timer_queue,
const typename Time_Traits::time_type& time, Handler handler, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_)
if (timer_queue.enqueue_timer(time, handler, token))
interrupter_.interrupt();
}
// Cancel the timer associated with the given token. Returns the number of
// handlers that have been posted or dispatched.
template <typename Time_Traits>
std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
{
asio::detail::mutex::scoped_lock lock(mutex_);
return timer_queue.cancel_timer(token);
}
private:
friend class task_io_service<select_reactor<Own_Thread> >;
// Run select once until interrupted or events are ready to be dispatched.
void run(bool block)
{
asio::detail::mutex::scoped_lock lock(mutex_);
// Dispatch any operation cancellations that were made while the select
// loop was not running.
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
except_op_queue_.dispatch_cancellations();
// Check if the thread is supposed to stop.
if (stop_thread_)
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
// We can return immediately if there's no work to do and the reactor is
// not supposed to block.
if (!block && read_op_queue_.empty() && write_op_queue_.empty()
&& except_op_queue_.empty() && all_timer_queues_are_empty())
{
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
return;
}
// Set up the descriptor sets.
fd_set_adapter read_fds;
read_fds.set(interrupter_.read_descriptor());
read_op_queue_.get_descriptors(read_fds);
fd_set_adapter write_fds;
write_op_queue_.get_descriptors(write_fds);
fd_set_adapter except_fds;
except_op_queue_.get_descriptors(except_fds);
socket_type max_fd = read_fds.max_descriptor();
if (write_fds.max_descriptor() > max_fd)
max_fd = write_fds.max_descriptor();
if (except_fds.max_descriptor() > max_fd)
max_fd = except_fds.max_descriptor();
// Block on the select call without holding the lock so that new
// operations can be started while the call is executing.
timeval tv_buf = { 0, 0 };
timeval* tv = block ? get_timeout(tv_buf) : &tv_buf;
select_in_progress_ = true;
lock.unlock();
int retval = socket_ops::select(static_cast<int>(max_fd + 1),
read_fds, write_fds, except_fds, tv);
lock.lock();
select_in_progress_ = false;
// Block signals while dispatching operations.
asio::detail::signal_blocker sb;
// Reset the interrupter.
if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor()))
interrupter_.reset();
// Dispatch all ready operations.
if (retval > 0)
{
// Exception operations must be processed first to ensure that any
// out-of-band data is read before normal data.
except_op_queue_.dispatch_descriptors(except_fds, 0);
read_op_queue_.dispatch_descriptors(read_fds, 0);
write_op_queue_.dispatch_descriptors(write_fds, 0);
except_op_queue_.dispatch_cancellations();
read_op_queue_.dispatch_cancellations();
write_op_queue_.dispatch_cancellations();
}
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
timer_queues_[i]->dispatch_timers();
// Issue any pending cancellations.
for (size_t i = 0; i < pending_cancellations_.size(); ++i)
cancel_ops_unlocked(pending_cancellations_[i]);
pending_cancellations_.clear();
// Clean up operations. We must not hold the lock since the operations may
// make calls back into this reactor.
lock.unlock();
read_op_queue_.cleanup_operations();
write_op_queue_.cleanup_operations();
except_op_queue_.cleanup_operations();
}
// Run the select loop in the thread.
void run_thread()
{
asio::detail::mutex::scoped_lock lock(mutex_);
while (!stop_thread_)
{
lock.unlock();
run(true);
lock.lock();
}
}
// Entry point for the select loop thread.
static void call_run_thread(select_reactor* reactor)
{
reactor->run_thread();
}
// Interrupt the select loop.
void interrupt()
{
interrupter_.interrupt();
}
// Check if all timer queues are empty.
bool all_timer_queues_are_empty() const
{
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
if (!timer_queues_[i]->empty())
return false;
return true;
}
// Get the timeout value for the select call.
timeval* get_timeout(timeval& tv)
{
if (all_timer_queues_are_empty())
return 0;
// By default we will wait no longer than 5 minutes. This will ensure that
// any changes to the system clock are detected after no longer than this.
boost::posix_time::time_duration minimum_wait_duration
= boost::posix_time::minutes(5);
for (std::size_t i = 0; i < timer_queues_.size(); ++i)
{
boost::posix_time::time_duration wait_duration
= timer_queues_[i]->wait_duration();
if (wait_duration < minimum_wait_duration)
minimum_wait_duration = wait_duration;
}
if (minimum_wait_duration > boost::posix_time::time_duration())
{
tv.tv_sec = minimum_wait_duration.total_seconds();
tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000;
}
else
{
tv.tv_sec = 0;
tv.tv_usec = 0;
}
return &tv;
}
// Cancel all operations associated with the given descriptor. The do_cancel
// function of the handler objects will be invoked. This function does not
// acquire the select_reactor's mutex.
void cancel_ops_unlocked(socket_type descriptor)
{
bool interrupt = read_op_queue_.cancel_operations(descriptor);
interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
if (interrupt)
interrupter_.interrupt();
}
// Mutex to protect access to internal data.
asio::detail::mutex mutex_;
// Whether the select loop is currently running or not.
bool select_in_progress_;
// The interrupter is used to break a blocking select call.
select_interrupter interrupter_;
// The queue of read operations.
reactor_op_queue<socket_type> read_op_queue_;
// The queue of write operations.
reactor_op_queue<socket_type> write_op_queue_;
// The queue of exception operations.
reactor_op_queue<socket_type> except_op_queue_;
// The timer queues.
std::vector<timer_queue_base*> timer_queues_;
// The descriptors that are pending cancellation.
std::vector<socket_type> pending_cancellations_;
// Does the reactor loop thread need to stop.
bool stop_thread_;
// The thread that is running the reactor loop.
asio::detail::thread* thread_;
// Whether the service has been shut down.
bool shutdown_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_DETAIL_SELECT_REACTOR_HPP

Some files were not shown because too many files have changed in this diff Show More