*** empty log message ***

This commit is contained in:
Arvid Norberg 2003-12-21 17:28:27 +00:00
parent cb89ccf6be
commit 5ac9f67f23
11 changed files with 361 additions and 61 deletions

15
Jamfile
View File

@ -1,5 +1,3 @@
SOURCES =
entry.cpp
peer_connection.cpp
@ -16,15 +14,18 @@ SOURCES =
sha1.c
;
lib torrent
: src/$(SOURCES)
zlib//zlib
$(BOOST_ROOT)/libs/filesystem/build//boost_filesystem
$(BOOST_ROOT)/libs/thread/build//boost_thread
# <lib>zlib/zlib
# zlib//zlib
# <file>boost_filesystem.lib
# <file>boost_date_time.lib
# <file>boost_thread.dll
: <include>$(BOOST_ROOT)
<sysinclude>$(BOOST_ROOT)
<include>./include
<include>./
<include>./zlib
<threading>multi
<link>static
: debug release
@ -33,7 +34,7 @@ lib torrent
exe client_test
: examples/client_test.cpp
torrent
<lib>torrent
: <include>$(BOOST_ROOT)
<sysinclude>$(BOOST_ROOT)
<include>./include

View File

@ -43,23 +43,24 @@
<li><a class="reference" href="#http-settings" id="id21" name="id21">http_settings</a></li>
<li><a class="reference" href="#big-number" id="id22" name="id22">big_number</a></li>
<li><a class="reference" href="#hasher" id="id23" name="id23">hasher</a></li>
<li><a class="reference" href="#exceptions" id="id24" name="id24">exceptions</a><ul>
<li><a class="reference" href="#invalid-handle" id="id25" name="id25">invalid_handle</a></li>
<li><a class="reference" href="#duplicate-torrent" id="id26" name="id26">duplicate_torrent</a></li>
<li><a class="reference" href="#invalid-encoding" id="id27" name="id27">invalid_encoding</a></li>
<li><a class="reference" href="#type-error" id="id28" name="id28">type_error</a></li>
<li><a class="reference" href="#invalid-torrent-file" id="id29" name="id29">invalid_torrent_file</a></li>
<li><a class="reference" href="#fingerprint" id="id24" name="id24">fingerprint</a></li>
<li><a class="reference" href="#exceptions" id="id25" name="id25">exceptions</a><ul>
<li><a class="reference" href="#invalid-handle" id="id26" name="id26">invalid_handle</a></li>
<li><a class="reference" href="#duplicate-torrent" id="id27" name="id27">duplicate_torrent</a></li>
<li><a class="reference" href="#invalid-encoding" id="id28" name="id28">invalid_encoding</a></li>
<li><a class="reference" href="#type-error" id="id29" name="id29">type_error</a></li>
<li><a class="reference" href="#invalid-torrent-file" id="id30" name="id30">invalid_torrent_file</a></li>
</ul>
</li>
<li><a class="reference" href="#example-usage" id="id30" name="id30">example usage</a><ul>
<li><a class="reference" href="#dump-torrent" id="id31" name="id31">dump_torrent</a></li>
<li><a class="reference" href="#simple-client" id="id32" name="id32">simple client</a></li>
<li><a class="reference" href="#example-usage" id="id31" name="id31">example usage</a><ul>
<li><a class="reference" href="#dump-torrent" id="id32" name="id32">dump_torrent</a></li>
<li><a class="reference" href="#simple-client" id="id33" name="id33">simple client</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference" href="#feedback" id="id33" name="id33">Feedback</a></li>
<li><a class="reference" href="#aknowledgements" id="id34" name="id34">Aknowledgements</a></li>
<li><a class="reference" href="#feedback" id="id34" name="id34">Feedback</a></li>
<li><a class="reference" href="#aknowledgements" id="id35" name="id35">Aknowledgements</a></li>
</ul>
</div>
<div class="section" id="introduction">
@ -88,9 +89,10 @@ The current state includes the following features:</p>
<li>queues torrents for file check, instead of checking all of them in parallel.</li>
<li>uses separate threads for checking files and for main downloader, with a fool-proof
thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).</li>
<li>can limit the upload bandwidth usage</li>
<li>can limit the upload bandwidth usage and the maximum number of unchoked peers</li>
<li>piece-wise file allocation</li>
<li>upload rate limit, balanced depending on download speed and upload bandwidth</li>
<li>tries to maintain a 1:1 share ratio between all peers but also shifts free
download to peers as free upload. To maintain a global 1:1 ratio.</li>
</ul>
</blockquote>
<p>Functions that are yet to be implemented:</p>
@ -115,6 +117,12 @@ boost.filesystem boost.date_time and various other boost libraries and zlib.</p>
<li>Linux x86 (debian) GCC 3.0</li>
</ul>
</blockquote>
<p>It does not compile on</p>
<blockquote>
<ul class="simple">
<li>GCC 2.95</li>
</ul>
</blockquote>
</div>
<div class="section" id="building">
<h1><a class="toc-backref" href="#id8" name="building">building</a></h1>
@ -158,7 +166,8 @@ the <tt class="literal"><span class="pre">session</span></tt>, it contains the m
<pre class="literal-block">
class session: public boost::noncopyable
{
session(int listen_port, const std::string&amp; fingerprint = std::string());
session(int listen_port, const fingerprint&amp; print);
session(int listen_port);
torrent_handle add_torrent(const torrent_info&amp; t, const std::string&amp; save_path);
void remove_torrent(const torrent_handle&amp; h);
@ -179,9 +188,11 @@ the tracker that we've stopped participating in the swarm.</p>
<p>If the torrent you are trying to add already exists in the session (is either queued
for checking, being checked or downloading) <tt class="literal"><span class="pre">add_torrent()</span></tt> will throw
<tt class="literal"><span class="pre">duplicate_torrent</span></tt> which derives from <tt class="literal"><span class="pre">std::exception</span></tt>.</p>
<p><tt class="literal"><span class="pre">fingerprint</span></tt> is a short string that will be used in the peer_id to
identify the client. If the string is longer than 7 characters it will
be trimmed down to 7 characters. The default is an empty string.</p>
<p>The difference between the two constructors is that one of them takes a fingerprint
as argument. If this is ommited, the client will get a default fingerprint stating
the version of libtorrent. The fingerprint is a short string that will be used in
the peer-id to identify the client and the client's version. For more details see the
fingerprint class.</p>
<p><tt class="literal"><span class="pre">set_upload_rate_limit()</span></tt> set the maximum number of bytes allowed to be
sent to peers per second. This bandwidth is distributed among all the peers. If
you don't want to limit upload rate, you can set this to -1 (the default).</p>
@ -555,6 +566,8 @@ struct peer_info
int upload_limit;
int upload_ceiling;
int load_balancing;
int downloading_piece_index;
int downloading_block_index;
int downloading_progress;
@ -586,6 +599,11 @@ should sum up to the upload limit set by <tt class="literal"><span class="pre">s
<p><tt class="literal"><span class="pre">upload_ceiling</span></tt> is the current maximum allowed upload rate given the cownload
rate and share ratio. If the global upload rate is inlimited, the <tt class="literal"><span class="pre">upload_limit</span></tt>
for every peer will be the same as their <tt class="literal"><span class="pre">upload_ceiling</span></tt>.</p>
<p><tt class="literal"><span class="pre">load_balancing</span></tt> is a measurment of the balancing of free download (that we get)
and free upload that we give. Every peer gets a certain amount of free upload, but
this member says how much <em>extra</em> free upload this peer has got. If it is a negative
number it means that this was a peer from which we have got this amount of free
download.</p>
<p>You can know which piece, and which part of that piece, that is currently being
downloaded from a specific peer by looking at the next four members.
<tt class="literal"><span class="pre">downloading_piece_index</span></tt> is the index of the piece that is currently being downloaded.
@ -725,12 +743,61 @@ call <tt class="literal"><span class="pre">reset()</span></tt> to reinitialize i
<p>The sha1-algorithm used was implemented by Steve Reid and released as public domain.
For more info, see <tt class="literal"><span class="pre">src/sha1.c</span></tt>.</p>
</div>
<div class="section" id="fingerprint">
<h2><a class="toc-backref" href="#id24" name="fingerprint">fingerprint</a></h2>
<p>The fingerprint class represents information about a client and its version. It is used
to encode this information into the client's peer id.</p>
<p>This is the class declaration:</p>
<pre class="literal-block">
struct fingerprint
{
fingerprint(const char* id_string, int major, int minor, int revision, int tag);
std::string to_string() const;
char id[2];
char major_version;
char minor_version;
char revision_version;
char tag_version;
};
</pre>
<p>The constructor takes a <tt class="literal"><span class="pre">const</span> <span class="pre">char*</span></tt> that should point to a string constant containing
exactly two characters. These are the characters that should be unique for your client. Make
sure not to clash with anybody else. Here are some taken id's:</p>
<table border class="table">
<colgroup>
<col width="30%" />
<col width="70%" />
</colgroup>
<thead valign="bottom">
<tr><th>id chars</th>
<th>client</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>'AZ'</td>
<td>Azureus</td>
</tr>
<tr><td>'LT'</td>
<td>libtorrent (default)</td>
</tr>
<tr><td>'BX'</td>
<td>BittorrentX</td>
</tr>
</tbody>
</table>
<p>The <tt class="literal"><span class="pre">major</span></tt>, <tt class="literal"><span class="pre">minor</span></tt>, <tt class="literal"><span class="pre">revision</span></tt> and <tt class="literal"><span class="pre">tag</span></tt> parameters are used to identify the
version of your client. All these numbers must be within the range [0, 9].</p>
<p><tt class="literal"><span class="pre">to_string()</span></tt> will generate the actual string put in the peer-id, and return it.</p>
</div>
<div class="section" id="exceptions">
<h2><a class="toc-backref" href="#id24" name="exceptions">exceptions</a></h2>
<h2><a class="toc-backref" href="#id25" name="exceptions">exceptions</a></h2>
<p>There are a number of exceptions that can be thrown from different places in libtorrent,
here's a complete list with description.</p>
<div class="section" id="invalid-handle">
<h3><a class="toc-backref" href="#id25" name="invalid-handle">invalid_handle</a></h3>
<h3><a class="toc-backref" href="#id26" name="invalid-handle">invalid_handle</a></h3>
<p>This exception is thrown when querying information from a <tt class="literal"><span class="pre">torrent_handle</span></tt> that hasn't
been initialized or that has become invalid.</p>
<pre class="literal-block">
@ -741,7 +808,7 @@ struct invalid_handle: std::exception
</pre>
</div>
<div class="section" id="duplicate-torrent">
<h3><a class="toc-backref" href="#id26" name="duplicate-torrent">duplicate_torrent</a></h3>
<h3><a class="toc-backref" href="#id27" name="duplicate-torrent">duplicate_torrent</a></h3>
<p>This is thrown by <tt class="literal"><span class="pre">session::add_torrent()</span></tt> if the torrent already has been added to
the session.</p>
<pre class="literal-block">
@ -752,7 +819,7 @@ struct duplicate_torrent: std::exception
</pre>
</div>
<div class="section" id="invalid-encoding">
<h3><a class="toc-backref" href="#id27" name="invalid-encoding">invalid_encoding</a></h3>
<h3><a class="toc-backref" href="#id28" name="invalid-encoding">invalid_encoding</a></h3>
<p>This is thrown by <tt class="literal"><span class="pre">bdecode()</span></tt> if the input data is not a valid bencoding.</p>
<pre class="literal-block">
struct invalid_encoding: std::exception
@ -762,7 +829,7 @@ struct invalid_encoding: std::exception
</pre>
</div>
<div class="section" id="type-error">
<h3><a class="toc-backref" href="#id28" name="type-error">type_error</a></h3>
<h3><a class="toc-backref" href="#id29" name="type-error">type_error</a></h3>
<p>This is thrown from the accessors of <tt class="literal"><span class="pre">entry</span></tt> if the data type of the <tt class="literal"><span class="pre">entry</span></tt> doesn't
match the type you want to extract from it.</p>
<pre class="literal-block">
@ -773,7 +840,7 @@ struct type_error: std::runtime_error
</pre>
</div>
<div class="section" id="invalid-torrent-file">
<h3><a class="toc-backref" href="#id29" name="invalid-torrent-file">invalid_torrent_file</a></h3>
<h3><a class="toc-backref" href="#id30" name="invalid-torrent-file">invalid_torrent_file</a></h3>
<p>This exception is thrown from the constructor of <tt class="literal"><span class="pre">torrent_info</span></tt> if the given bencoded information
doesn't meet the requirements on what information has to be present in a torrent file.</p>
<pre class="literal-block">
@ -785,9 +852,9 @@ struct invalid_torrent_file: std::exception
</div>
</div>
<div class="section" id="example-usage">
<h2><a class="toc-backref" href="#id30" name="example-usage">example usage</a></h2>
<h2><a class="toc-backref" href="#id31" name="example-usage">example usage</a></h2>
<div class="section" id="dump-torrent">
<h3><a class="toc-backref" href="#id31" name="dump-torrent">dump_torrent</a></h3>
<h3><a class="toc-backref" href="#id32" name="dump-torrent">dump_torrent</a></h3>
<p>This is an example of a program that will take a torrent-file as a parameter and
print information about it to std out:</p>
<pre class="literal-block">
@ -851,7 +918,7 @@ int main(int argc, char* argv[])
</pre>
</div>
<div class="section" id="simple-client">
<h3><a class="toc-backref" href="#id32" name="simple-client">simple client</a></h3>
<h3><a class="toc-backref" href="#id33" name="simple-client">simple client</a></h3>
<p>This is a simple client. It doesn't have much output to keep it simple:</p>
<pre class="literal-block">
#include &lt;iostream&gt;
@ -880,7 +947,7 @@ int main(int argc, char* argv[])
try
{
session s(6881, &quot;E\x1&quot;);
session s(6881);
std::ifstream in(argv[1], std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
@ -904,14 +971,15 @@ int main(int argc, char* argv[])
</div>
</div>
<div class="section" id="feedback">
<h1><a class="toc-backref" href="#id33" name="feedback">Feedback</a></h1>
<h1><a class="toc-backref" href="#id34" name="feedback">Feedback</a></h1>
<p>There's a <a class="reference" href="http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss">mailing list</a>.</p>
<p>You can usually find me as hydri in <tt class="literal"><span class="pre">#btports</span> <span class="pre">&#64;</span> <span class="pre">irc.freenode.net</span></tt>.</p>
</div>
<div class="section" id="aknowledgements">
<h1><a class="toc-backref" href="#id34" name="aknowledgements">Aknowledgements</a></h1>
<h1><a class="toc-backref" href="#id35" name="aknowledgements">Aknowledgements</a></h1>
<p>Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003</p>
<p>Contributions by Magnus Jonsson</p>
<p>Thanks to Reimond Retz for bugfixes, suggestions and testing</p>
<p>Project is hosted by sourceforge.</p>
<p><a class="reference" href="http://sourceforge.net"><img alt="sf_logo" src="http://sourceforge.net/sflogo.php?group_id=7994" /></a></p>
</div>

View File

@ -832,6 +832,8 @@ sure not to clash with anybody else. Here are some taken id's:
+----------+-----------------------+
| 'LT' | libtorrent (default) |
+----------+-----------------------+
| 'BX' | BittorrentX |
+----------+-----------------------+
The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the
version of your client. All these numbers must be within the range [0, 9].

View File

@ -302,8 +302,13 @@ int main(int argc, char* argv[])
if (progress > j) out << "#";
else out << "-";
}
out << "\n";
out << " ";
}
else
{
for (int i = 0; i < 19; ++i) out << " ";
}
out << identify_client(i->id) << "\n";
}
out << "___________________________________\n";

View File

@ -65,6 +65,12 @@ namespace libtorrent
return false;
}
unsigned char& operator[](int i)
{ assert(i >= 0 && i < number_size); return m_number[i]; }
unsigned char operator[](int i) const
{ assert(i >= 0 && i < number_size); return m_number[i]; }
typedef const unsigned char* const_iterator;
typedef unsigned char* iterator;

View File

@ -182,7 +182,7 @@ namespace libtorrent
struct http_settings;
std::string extract_fingerprint(const peer_id& p);
std::string identify_client(const peer_id& p);
class session: public boost::noncopyable, detail::eh_initializer
{

View File

@ -84,6 +84,9 @@ namespace libtorrent
// the number of bytes of the file we have
std::size_t total_done;
// TODO: add flag that says if there have
// been any incoming connections
};
struct partial_piece_info

View File

@ -485,6 +485,7 @@ namespace libtorrent
// this means we're already connected
// to this peer. don't connect to
// it again.
assert(i->connection->associated_torrent() == m_torrent);
return;
}

View File

@ -62,6 +62,8 @@ namespace std
};
#endif
// TODO: enable floating point exceptions in debug mode!
namespace
{
@ -486,6 +488,10 @@ namespace libtorrent
{
// (*m_logger) << "readable: " << p->first->sender().as_string() << "\n";
p->second->receive_data();
#ifndef NDEBUG
assert_invariant();
#endif
}
catch(std::exception& e)
{
@ -684,7 +690,19 @@ namespace libtorrent
i != m_connections.end();
++i)
{
assert(i->second->has_data() == m_selector.is_writability_monitored(i->first));
if (i->second->has_data() != m_selector.is_writability_monitored(i->first))
{
std::ofstream error_log("error.log", std::ios_base::app);
boost::shared_ptr<peer_connection> p = i->second;
error_log << "session_imple::assert_invariant()\n"
"peer_connection::has_data() != is_writability_monitored()\n";
error_log << "peer_connection::has_data() " << p->has_data() << "\n";
error_log << "peer_connection::send_quota_left " << p->send_quota_left() << "\n";
error_log << "peer_connection::send_quota " << p->send_quota() << "\n";
error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n";
error_log.flush();
assert(false);
}
if (i->second->associated_torrent())
{
assert(i->second->associated_torrent()
@ -849,25 +867,200 @@ namespace libtorrent
return m_impl.m_alerts.get();
}
// TODO: document
// TODO: if the first 4 charachters are printable
// maybe they should be considered a fingerprint?
std::string extract_fingerprint(const peer_id& p)
namespace
{
std::string ret;
const unsigned char* c = p.begin();
while (c != p.end() && *c != 0)
{
if (std::isprint(*c))
ret += *c;
else if (*c <= 9)
ret += '0'+ *c;
else
return std::string();
++c;
}
if (c == p.end()) return std::string();
return ret;
}
// 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);
peer_id::const_iterator i = id.begin();
if (*i != '-') return boost::optional<fingerprint>();
++i;
for (int j = 0; j < 2; ++j)
{
if (!std::isprint(*i)) return boost::optional<fingerprint>();
ret.id[j] = *i;
++i;
}
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.major_version = *i - '0';
++i;
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.minor_version = *i - '0';
++i;
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.revision_version = *i - '0';
++i;
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.tag_version = *i - '0';
++i;
if (*i != '-') return boost::optional<fingerprint>();
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);
peer_id::const_iterator i = id.begin();
if (!std::isprint(*i)) return boost::optional<fingerprint>();
ret.id[0] = *i;
ret.id[1] = 0;
++i;
if (id[8] == 45)
{
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.major_version = *i - '0';
++i;
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.minor_version = *i - '0';
++i;
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.revision_version = *i - '0';
}
else if (id[0] == 0)
{
if (*i > 127) return boost::optional<fingerprint>();
ret.major_version = *i;
++i;
if (*i > 127) return boost::optional<fingerprint>();
ret.minor_version = *i;
++i;
if (*i > 127) return boost::optional<fingerprint>();
ret.revision_version = *i;
}
else
return boost::optional<fingerprint>();
ret.tag_version = 0;
return boost::optional<fingerprint>(ret);
}
} // namespace unnamed
// TODO: document
std::string identify_client(const peer_id& p)
{
peer_id::const_iterator PID = p.begin();
boost::optional<fingerprint> f;
// look for azureus style id
f = parse_az_style(p);
if (f)
{
std::stringstream identity;
// azureus
if (std::equal(f->id, f->id+2, "AZ"))
identity << "Azureus ";
// BittorrentX
else if (std::equal(f->id, f->id+2, "BX"))
identity << "BittorrentX ";
// libtorrent
else if (std::equal(f->id, f->id+2, "LT"))
identity << "libtorrent ";
// unknown client
else
identity << std::string(f->id, f->id+2) << " ";
identity << (int)f->major_version
<< "." << (int)f->minor_version
<< "." << (int)f->revision_version
<< "." << (int)f->tag_version;
return identity.str();
}
// look for shadow style id
f = parse_shadow_style(p);
if (f)
{
std::stringstream identity;
// Shadow
if (std::equal(f->id, f->id+1, "S"))
identity << "Shadow ";
// UPnP
else if (std::equal(f->id, f->id+1, "U"))
identity << "UPnP ";
// unknown client
else
identity << std::string(f->id, f->id+1) << " ";
identity << (int)f->major_version
<< "." << (int)f->minor_version
<< "." << (int)f->revision_version;
return identity.str();
}
// ----------------------
// non standard encodings
// ----------------------
if (std::equal(PID + 5, PID + 5 + 8, "Azureus"))
{
return "Azureus 2.0.3.2";
}
if (std::equal(PID, PID + 11, "DansClient"))
{
return "XanTorrent";
}
if (std::equal(PID, PID + 7, "btfans"))
{
return "BitComet";
}
if (std::equal(PID, PID + 8, "turbobt"))
{
return "TurboBT";
}
if (std::equal(PID, PID + 13, "\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"))
{
return "Experimental 3.1";
}
return "Generic";
}
}

View File

@ -466,6 +466,8 @@ namespace libtorrent {
m_pimpl->write(buf, piece_index, offset, size);
}
// TODO: must handle the case where some hashes are identical
// correctly
void piece_manager::impl::check_pieces(
boost::mutex& mutex
, detail::piece_checker_data& data
@ -612,6 +614,8 @@ namespace libtorrent {
int found_piece = -1;
// TODO: there's still potential problems if some
// pieces have the same hash
for (int i = current_slot; i < m_info.num_pieces(); ++i)
{
if (pieces[i] && i != current_slot) continue;
@ -620,7 +624,10 @@ namespace libtorrent {
i == m_info.num_pieces() - 1]->get();
if (hash == m_info.hash_for_piece(i))
{
found_piece = i;
if (i == current_slot) break;
}
}
if (found_piece != -1)

View File

@ -73,8 +73,22 @@ namespace
int calculate_block_size(const torrent_info& i)
{
// TODO: if blocks_per_piece > 128 increase block-size
return 16*1024;
const int default_block_size = 16 * 1024;
// if pieces are too small, adjust the block size
if (i.piece_length() < default_block_size)
{
return i.piece_length();
}
// if pieces are too large, adjust the block size
if (i.piece_length() / default_block_size > 128)
{
return i.piece_length() / 128;
}
// otherwise, go with the default
return default_block_size;
}
@ -199,7 +213,7 @@ namespace libtorrent
{
std::cout << " " << std::setfill(' ') << std::setw(16) << i->ip
<< " " << std::setw(5) << std::dec << i->port << " "
<< i->id << " " << extract_fingerprint(i->id) << "\n";
<< i->id << " " << identify_client(i->id) << "\n";
}
std::cout << std::setfill(' ');