I am trying to keep a list of connected clients in asio. I have adapted the chat server example from the docs (http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/example/cpp03/chat/chat_server.cpp) and here's the important part of what I ended up with:
#include <iostream>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <set>
using boost::asio::ip::tcp;
class tcp_connection;
std::set<boost::shared_ptr<tcp_connection>> clients;
void add_client(boost::shared_ptr<tcp_connection> client)
{
clients.insert(client);
}
class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
public:
tcp_connection(boost::asio::io_service& io_service) : socket_(io_service)
{
}
tcp::socket socket_;
void start()
{
add_client(shared_from_this());
}
tcp::socket& socket()
{
return socket_;
}
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), 6767))
{
tcp_connection* new_connection = new tcp_connection(io_service_);
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::start_accept, this, new_connection,
boost::asio::placeholders::error));
}
private:
void start_accept(tcp_connection* new_connection,
const boost::system::error_code& error)
{
if (!error)
{
new_connection->start();
new_connection = new tcp_connection(io_service_);
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::start_accept, this, new_connection,
boost::asio::placeholders::error));
}
}
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
At the call to shared_from_this()
, my server crashes with the message:
Exception: tr1::bad_weak_ptr
I have done some searching and it appears shared_from_this()
is pretty particular, but I can't seem to find exactly what I need to change.
Best Answer
John Zwinck's essential analysis is spot on:
However, his advice seems completely beside the point and dangerous in Asio code.
You should solve this by - indeed - not handling raw pointers to
tcp_connection
in the first place but always usingshared_ptr
instead.boost::bind
has the awesome feature that it binds toshared_ptr<>
just fine so it automagically keeps the pointed to object alive as long as some asynchronous operation is operating on it.This - in your sample code - means you don't need the
clients
vector, going the opposite way from John's answer:I've included a sample that makes
tcp_connection
do some trivial work (it loops writing 'hello world' to the client each second, until the client drops the connection. When it does, you can see the destructor of thetcp_connection
operation being run:Live On Coliru
Typical output: