ASIO Attempting to reference a deleted function - c++

I have the following error:
Error C2280 'Sender::Sender(const Sender &)': attempting to reference a deleted function
This is my code:
#pragma once
#include <iostream>
#include <string>
#include "asio.hpp"
using namespace asio;
class Sender
{
public:
Sender(io_service& io_service,
const std::string& host,
const std::string& port) :
io_service_(io_service),
socket_(io_service, ip::udp::endpoint(ip::udp::v4(), 0))
{
ip::udp::resolver resolver(io_service_);
ip::udp::resolver::query query(ip::udp::v4(), host, port);
ip::udp::resolver::iterator iter = resolver.resolve(query);
endpoint_ = *iter;
};
~Sender()
{
socket_.close();
};
void operator()()
{
std::string msg = "";
for (;;)
{
std::cout << "Message: ";
std::cin >> msg;
std::cout << "The value you entered is " << msg << std::endl;
}
}
private:
io_service& io_service_;
ip::udp::socket socket_;
ip::udp::endpoint endpoint_;
};
And I'm calling it like this:
io_service io_service;
Sender s{ io_service, "localhost", "1337" };
std::thread sender_thread{ s };
sender_thread.join();
I know that it's because one of the private members can't be copied, but I can't figure out how to fix it! I don't want to implement a copy constructor.

Sender is moveable but not copyable because boost::asio's socket is moveable but not copyable (see documentation).
You will need to std::move() your sender into the thread's constructor.

Related

Boost ASIO deserialization wrong values

when I try to deserialize a struct on the server, I am receiving incorrect values from initial transmission. I created a struct with member variables of the same type (2 and 1); however, when transmitting receive zeros and unsure why.
Does the incoming data in connection.hpp need to be of the same type, which I am sending? Any suggestions ?
Client:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
#include "connection.hpp" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include "info.hpp"
class client
{
public:
/// Constructor starts the asynchronous connect operation.
client(boost::asio::io_service& io_service,
const std::string& host, const std::string& service)
: connection_(io_service)
{
Packet p;
p.a = 2;
p.b = 1;
packet_.push_back(p);
// Resolve the host name into an IP address.
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(host, service);
boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
resolver.resolve(query);
// Start an asynchronous connect operation.
boost::asio::async_connect(connection_.socket(), endpoint_iterator,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error));
}
/// Handle completion of a connect operation.
void handle_connect(const boost::system::error_code& e)
{
if (!e)
{
connection_.async_write(packet_,
boost::bind(&client::handle_write, this,
boost::asio::placeholders::error));
}
else
{
std::cerr << e.message() << std::endl;
}
}
void handle_write(const boost::system::error_code& e)
{
}
private:
connection connection_;
std::vector<Packet> packet_;
};
}
int main(int argc, char* argv[])
{
try
{
// Check command line arguments.
if (argc != 3)
{
std::cerr << "Usage: client <host> <port>" << std::endl;
return 1;
}
boost::asio::io_service io_service;
s11n_example::client client(io_service, argv[1], argv[2]);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Info:
#ifndef Packet_HPP
#define Packet_HPP
namespace s11n_example {
struct Packet
{
int a;
int b;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
int a;
int b;
}
};
} // namespace s11n_example
#endif
Server:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include "connection.hpp" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include "info.hpp"
namespace s11n_example {
/// Serves stock quote information to any client that connects to it.
class server
{
public:
/// Constructor opens the acceptor and starts waiting for the first incoming
/// connection.
server(boost::asio::io_service& io_service, unsigned short port)
: acceptor_(io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
// Create the data to be sent to each client.
// Start an accept operation for a new connection.
connection_ptr new_conn(new connection(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
/// Handle completion of a accept operation.
void handle_accept(const boost::system::error_code& e, connection_ptr conn)
{
if (!e)
{
conn->async_read(packet_,
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error, conn));
}
// Start an accept operation for a new connection.
connection_ptr new_conn(new connection(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
/// Handle completion of a write operation.
void handle_read(const boost::system::error_code& e, connection_ptr conn)
{
// Print out the data that was received.
for (std::size_t i = 0; i < packet_.size(); ++i)
std::cout << packet_[i].a << "\n";
}
private:
/// The acceptor object used to accept incoming socket connections.
boost::asio::ip::tcp::acceptor acceptor_;
/// The data to be sent to each client.
std::vector<Packet> packet_;
};
} // namespace s11n_example
int main(int argc, char* argv[])
{
try
{
// Check command line arguments.
if (argc != 2)
{
std::cerr << "Usage: server <port>" << std::endl;
return 1;
}
unsigned short port = boost::lexical_cast<unsigned short>(argv[1]);
boost::asio::io_service io_service;
s11n_example::server server(io_service, port);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Connection
#ifndef SERIALIZATION_CONNECTION_HPP
#define SERIALIZATION_CONNECTION_HPP
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <iomanip>
#include <string>
#include <sstream>
#include <vector>
namespace s11n_example {
/// The connection class provides serialization primitives on top of a socket.
/**
* Each message sent using this class consists of:
* #li An 8-byte header containing the length of the serialized data in
* hexadecimal.
* #li The serialized data.
*/
class connection
{
public:
/// Constructor.
connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
/// Get the underlying socket. Used for making a connection or for accepting
/// an incoming connection.
boost::asio::ip::tcp::socket& socket()
{
return socket_;
}
/// Asynchronously write a data structure to the socket.
template <typename T, typename Handler>
void async_write(const T& t, Handler handler)
{
// Serialize the data first so we know how large it is.
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << t;
outbound_data_ = archive_stream.str();
// Format the header.
std::ostringstream header_stream;
header_stream << std::setw(header_length)
<< std::hex << outbound_data_.size();
if (!header_stream || header_stream.str().size() != header_length)
{
// Something went wrong, inform the caller.
boost::system::error_code error(boost::asio::error::invalid_argument);
socket_.get_io_service().post(boost::bind(handler, error));
return;
}
outbound_header_ = header_stream.str();
// Write the serialized data to the socket. We use "gather-write" to send
// both the header and the data in a single write operation.
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outbound_header_));
buffers.push_back(boost::asio::buffer(outbound_data_));
boost::asio::async_write(socket_, buffers, handler);
}
/// Asynchronously read a data structure from the socket.
template <typename T, typename Handler>
void async_read(T& t, Handler handler)
{
// Issue a read operation to read exactly the number of bytes in a header.
void (connection::*f)(
const boost::system::error_code&,
T&, boost::tuple<Handler>)
= &connection::handle_read_header<T, Handler>;
boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
boost::bind(f,
this, boost::asio::placeholders::error, boost::ref(t),
boost::make_tuple(handler)));
}
/// Handle a completed read of a message header. The handler is passed using
/// a tuple since boost::bind seems to have trouble binding a function object
/// created using boost::bind as a parameter.
template <typename T, typename Handler>
void handle_read_header(const boost::system::error_code& e,
T& t, boost::tuple<Handler> handler)
{
if (e)
{
boost::get<0>(handler)(e);
}
else
{
// Determine the length of the serialized data.
std::istringstream is(std::string(inbound_header_, header_length));
std::size_t inbound_data_size = 0;
if (!(is >> std::hex >> inbound_data_size))
{
// Header doesn't seem to be valid. Inform the caller.
boost::system::error_code error(boost::asio::error::invalid_argument);
boost::get<0>(handler)(error);
return;
}
// Start an asynchronous call to receive the data.
inbound_data_.resize(inbound_data_size);
void (connection::*f)(
const boost::system::error_code&,
T&, boost::tuple<Handler>)
= &connection::handle_read_data<T, Handler>;
boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
boost::bind(f, this,
boost::asio::placeholders::error, boost::ref(t), handler));
}
}
/// Handle a completed read of message data.
template <typename T, typename Handler>
void handle_read_data(const boost::system::error_code& e,
T& t, boost::tuple<Handler> handler)
{
if (e)
{
boost::get<0>(handler)(e);
}
else
{
// Extract the data structure from the data just received.
try
{
std::string archive_data(&inbound_data_[0], inbound_data_.size());
std::istringstream archive_stream(archive_data);
boost::archive::text_iarchive archive(archive_stream);
archive >> t;
}
catch (std::exception& e)
{
// Unable to decode data.
boost::system::error_code error(boost::asio::error::invalid_argument);
boost::get<0>(handler)(error);
return;
}
// Inform caller that data has been received ok.
boost::get<0>(handler)(e);
}
}
private:
/// The underlying socket.
boost::asio::ip::tcp::socket socket_;
/// The size of a fixed length header.
enum { header_length = 8 };
/// Holds an outbound header.
std::string outbound_header_;
/// Holds the outbound data.
std::string outbound_data_;
/// Holds an inbound header.
char inbound_header_[header_length];
/// Holds the inbound data.
std::vector<char> inbound_data_;
};
typedef boost::shared_ptr<connection> connection_ptr;
} // namespace s11n_example
#endif // SERIALIZATION_CONNECTION_HPP
Like last time, this function makes no sense:
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
int a;
int b;
}
It just declares to integers and doesn't even touch the archive. Like before the fix for that is to ... serialize the data:
struct Packet {
int a, b;
template <typename Ar>
void serialize(Ar& ar, unsigned) { ar& a& b; }
};
Also, like last time, you're using a very old version of the sample:
1.49 is very old. It's very unlikely that's actually the version you are using. Try a more recent version (like https://www.boost.org/doc/libs/1_80_0/doc/html/boost_asio/examples/cpp03_examples.html#boost_asio.examples.cpp03_examples.serialization) or the one matching your version of boost.

Avoiding global variables making boost asio socket wrapper

i have this socket-tcp.h with an wrapper of socket implementation using boost asio:
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
static const std::string PORT = "65432";
static const std::string HOST = "127.0.0.1";
struct Client
{
boost::asio::io_service& io_service;
boost::asio::ip::tcp::socket socket;
Client(boost::asio::io_service& svc, std::string const& host, std::string const& port)
: io_service(svc), socket(io_service)
{
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port));
boost::asio::connect(this->socket, endpoint);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
boost::asio::io_service svc;
Client client(svc, HOST, PORT);
class TcpSocket
{
private:
std::string HOST;
std::string PORT;
public:
TcpSocket (std::string const& host, std::string const& port): HOST{ host },PORT{ port }
{
}
void send(std::string const& message)
{
boost::thread t(client_thread,boost::ref(message),boost::ref(HOST),boost::ref(PORT));
t.join();
}
static void client_thread(std::string const& message,std::string const& host,std::string const& port)
{
client.send(message);
}
};
so that my main file looks like:
#include "socket-tcp.h"
int main()
{
TcpSocket socket(PORT,HOST);
std::string message = "socket implemented using global variables";
while (true)
{
socket.send(message);
}
}
I'm trying to figure out a way of implement this without the global variables
boost::asio::io_service svc;
Client client(svc, HOST, PORT);
such that TcpSocket be like:
class TcpSocket
{
//Object* myObject; // Will not try to call the constructor or do any initializing
//myObject = new Object(...); // Initialised now
private:
std::string HOST;
std::string PORT;
boost::asio::io_service svc;
public:
TcpSocket (std::string const& host, std::string const& port): HOST{ host },PORT{ port }
{
Client client(svc, HOST, PORT);
}
void send(std::string const& message)
{
boost::thread t(client_thread,boost::ref(message),boost::ref(HOST),boost::ref(PORT));
t.join();
}
static void client_thread(std::string const& message,std::string const& host,std::string const& port)
{
client.send(message);
}
};
but i end up with the runtime error:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): resolve: Service not found
is there a way to avoid using these global variables (objects), and keeping the same socket open all the time, without closing and opening it again at each new message?
I accept better implementations or suggestions for this wrapper, but the goal is to keep main as simple and clear as possible.
The "service not found" merely means that the port is not a valid service. That's because you swapped host and port parameters.
But that is the least of your worries.
You're playing fast and loose with object lifetimes. For example, you pass message all the way to another thread &&by reference** (std::string const&), but the object referenced lives on the stack (as a function argument) so that invokes Undefined Behaviour.
Besides, it's not clear how the thread (or client_thread) is supposed to access the Client instance (that your code constructs as a local variable in the TcpSocket constructor only). Let alone that it would be unsafe to share that client across threads.
Medium Fix
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
static const std::string HOST = "127.0.0.1";
static const std::string PORT = "65432";
using boost::asio::ip::tcp;
struct Client
{
tcp::socket socket;
//template <typename Executor>
using Executor = boost::asio::io_service&;
Client(Executor executor, std::string const& host, std::string const& port)
: socket(executor)
{
std::cout << std::quoted(host) << std::endl;
std::cout << std::quoted(port) << std::endl;
auto ep = tcp::resolver{socket.get_executor()}.resolve(host, port);
connect(socket, ep);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
class TcpSocket {
private:
std::string _host;
std::string _port;
boost::asio::io_service _svc;
public:
TcpSocket(std::string const& host, std::string const& port)
: _host{host}
, _port{port}
{
}
void send(std::string const& message)
{
boost::thread t( //
client_thread, boost::ref(_svc), message, _host, _port);
t.join();
}
static void client_thread( //
boost::asio::io_service& svc,
std::string message, // NOT A REFERENCE
std::string host, // same
std::string port)
{
Client client(svc, host, port);
client.send(message);
}
};
//#include "socket-tcp.h"
int main()
{
TcpSocket socket(HOST, PORT);
std::string const message = "socket implemented using global variables";
while (true) {
socket.send(message);
}
}
More Notes
The thread is by definition useless, since you join it immediately. But if you really wanted that, please consider c++11 style:
void send(std::string const& message)
{
boost::thread t([=, &_svc] {
Client client(_svc, _host, _port);
client.send(message);
});
t.join();
}
It makes far more sense to do it on the main thread:
void send(std::string const& message)
{
Client client(_svc, _host, _port);
client.send(message);
}
Or to use async IO. Note that you should probably prefer to pass executors rather than sharing references to an execution context. Also, prefer io_context because io_service is deprecated.
Here's a simplified program that still does the same: Live On Coliru
BONUS - Async With A Thread Pool
Seems like you wanted the messages to be delivered in async fashion. However, you didn't know how to get threads in the background. Regardless, it wouldn't be a safe idea to "just create a new thread" each time. The professional approach is to use a thread pool.
This example uses a ddefault boost::asio::thread_pool and throws in a strand (even though it's not currently required) and a Session object to hold all the state for a single message. Note that we now cache the resolver results. That can be a good thing ,or a bad thing depending on your application.
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/enable_shared_from_this.hpp>
using boost::asio::ip::tcp;
using boost::system::error_code;
class TcpClient {
public:
TcpClient(std::string const& host, std::string const& port)
: _ep(tcp::resolver{_io}.resolve(host, port))
{ }
void send(std::string message)
{
boost::make_shared<Session>( //
make_strand(_io.get_executor()), std::move(message), _ep)
->run();
}
~TcpClient() {
//_io.stop(); // optionally
_io.join();
}
private:
boost::asio::thread_pool _io;
tcp::resolver::results_type _ep ;
struct Session : boost::enable_shared_from_this<Session> {
Session(auto executor, std::string message,
tcp::resolver::results_type ep)
: _socket(executor)
, _message(std::move(message))
, _ep(ep)
{
}
void run() {
async_connect( //
_socket, _ep,
[this, self = shared_from_this()](error_code ec,
tcp::endpoint) {
async_write(_socket, boost::asio::buffer(_message),
[this, self](error_code ec, size_t) {});
});
}
tcp::socket _socket;
std::string _message;
tcp::resolver::results_type _ep;
};
};
//#include "socket-tcp.h"
int main()
{
TcpClient socket("127.0.0.1", "65432");
for (int i = 0; i<100; ++i) {
socket.send("socket implemented using async IO on thread pool " +
std::to_string(i) + "\n");
}
}

shared_from_this throws bad_weak_ptr with boost::asio

First, I have read all related questions listed.
They say, "you must have an existing shared_ptr to this before you can use shared_from_this." As far as I can see, there is no way I am violating that condition. I create the instance of Foo as a shared_ptr and enforced that it is always created as a shared_ptr. I then, stored the shared_ptr in a collection. Yet, I still get the bad_weak_ptr exception when shared_from_this is called.
#pragma once
#include <memory>
#include <vector>
//--------------------------------------------------------------------
class Foo : std::enable_shared_from_this<Foo>
{
public:
typedef std::shared_ptr<Foo> SharedPtr;
// Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this
static Foo::SharedPtr Create()
{
return Foo::SharedPtr(new Foo());
};
Foo(const Foo &) = delete;
Foo(Foo &&) = delete;
Foo & operator = (const Foo &) = delete;
Foo & operator = (Foo &&) = delete;
~Foo() {};
// We have to defer the start until we are fully constructed because we share_from_this()
void Start()
{
DoStuff();
}
private:
Foo() {}
void DoStuff()
{
auto self(shared_from_this());
}
};
//--------------------------------------------------------------------
int main()
{
std::vector<Foo::SharedPtr> foos;
Foo::SharedPtr foo = Foo::Create();
foos.emplace_back(foo);
foo->Start();
return 0;
}
You must inherit enable_shared_from_this with public specifier according to
Publicly inheriting from std::enable_shared_from_this provides the type T with a member function shared_from_this.
from http://en.cppreference.com/w/cpp/memory/enable_shared_from_this.
So write
class Foo : public std::enable_shared_from_this<Foo>
First off, you start the threads before ever posting work, so the io_service::run() is prone to complete before DoAccept is actually done.
Next, the base class must be PUBLIC for enable_shared_from_this to work:
class Connection : public std::enable_shared_from_this<Connection> {
Working self-contained code:
#include <iostream>
#include <mutex>
namespace SomeNamespace{
struct Logger {
enum { LOGGER_SEVERITY_INFO };
void Log(std::string const& msg, std::string const& file, unsigned line, int level) const {
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
std::cout << file << ":" << line << " level:" << level << " " << msg << "\n";
}
template <typename... Args>
void LogF(std::string const& msg, Args const&... args) const {
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
static char buf[2048];
snprintf(buf, sizeof(buf)-1, msg.c_str(), args...);
std::cout << buf << "\n";
}
static Logger &GetInstance() {
static Logger This;
return This;
}
};
} // namespace Somenamespace
#include <boost/asio.hpp>
#include <atomic>
#include <condition_variable>
#include <memory>
//--------------------------------------------------------------------
class ConnectionManager;
//--------------------------------------------------------------------
class Connection : public std::enable_shared_from_this<Connection> {
public:
typedef std::shared_ptr<Connection> SharedPtr;
// Ensure all instances are created as shared_ptr in order to fulfill requirements for shared_from_this
static Connection::SharedPtr Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket);
Connection(const Connection &) = delete;
Connection(Connection &&) = delete;
Connection &operator=(const Connection &) = delete;
Connection &operator=(Connection &&) = delete;
~Connection();
// We have to defer the start until we are fully constructed because we share_from_this()
void Start();
void Stop();
void Send(const std::vector<char> &data);
private:
ConnectionManager *m_owner;
boost::asio::ip::tcp::socket m_socket;
std::atomic<bool> m_stopped;
boost::asio::streambuf m_receiveBuffer;
mutable std::mutex m_sendMutex;
std::shared_ptr<std::vector<boost::asio::const_buffer> > m_sendBuffers;
bool m_sending;
std::vector<char> m_allReadData; // for testing
Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket);
void DoReceive();
void DoSend();
};
//--------------------------------------------------------------------
//#include "Connection.h"
//#include "ConnectionManager.h"
//**ConnectionManager.h **
//#pragma once
//#include "Connection.h"
// Boost Includes
#include <boost/asio.hpp>
// Standard Includes
#include <thread>
#include <vector>
//--------------------------------------------------------------------
class ConnectionManager {
public:
ConnectionManager(unsigned port, size_t numThreads);
ConnectionManager(const ConnectionManager &) = delete;
ConnectionManager(ConnectionManager &&) = delete;
ConnectionManager &operator=(const ConnectionManager &) = delete;
ConnectionManager &operator=(ConnectionManager &&) = delete;
~ConnectionManager();
void Start();
void Stop();
void OnConnectionClosed(Connection::SharedPtr connection);
protected:
boost::asio::io_service m_io_service;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_listenSocket;
std::vector<std::thread> m_threads;
mutable std::mutex m_connectionsMutex;
std::vector<Connection::SharedPtr> m_connections;
void IoServiceThreadProc();
void DoAccept();
};
//--------------------------------------------------------------------
#include <boost/bind.hpp>
#include <algorithm>
//--------------------------------------------------------------------
Connection::SharedPtr Connection::Create(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket &socket) {
return Connection::SharedPtr(new Connection(connectionManager, std::move(socket)));
}
//--------------------------------------------------------------------
Connection::Connection(ConnectionManager *connectionManager, boost::asio::ip::tcp::socket socket)
: m_owner(connectionManager), m_socket(std::move(socket)), m_stopped(false), m_receiveBuffer(), m_sendMutex(),
m_sendBuffers(), m_sending(false), m_allReadData() {}
//--------------------------------------------------------------------
Connection::~Connection() {
// Boost uses RAII, so we don't have anything to do. Let thier destructors take care of business
}
//--------------------------------------------------------------------
void Connection::Start() { DoReceive(); }
//--------------------------------------------------------------------
void Connection::Stop() {
// The entire connection class is only kept alive, because it is a shared pointer and always has a ref count
// as a consequence of the outstanding async receive call that gets posted every time we receive.
// Once we stop posting another receive in the receive handler and once our owner release any references to
// us, we will get destroyed.
m_stopped = true;
m_owner->OnConnectionClosed(shared_from_this());
}
//--------------------------------------------------------------------
void Connection::Send(const std::vector<char> &data) {
std::lock_guard<std::mutex> lock(m_sendMutex);
// If the send buffers do not exist, then create them
if (!m_sendBuffers) {
m_sendBuffers = std::make_shared<std::vector<boost::asio::const_buffer> >();
}
// Copy the data to be sent to the send buffers
m_sendBuffers->emplace_back(boost::asio::buffer(data));
DoSend();
}
//--------------------------------------------------------------------
void Connection::DoSend() {
// According to the boost documentation, we cannot issue an async_write while one is already outstanding
//
// If that is the case, it is OK, because we've added the data to be sent to a new set of buffers back in
// the Send method. Notice how the original buffer is moved, so therefore will be null below and how Send
// will create new buffers and accumulate data to be sent until we complete in the lamda
//
// When we complete in the lamda, if we have any new data to be sent, we call DoSend once again.
//
// It is important though, that DoSend is only called from the lambda below and the Send method.
if (!m_sending && m_sendBuffers) {
m_sending = true;
auto copy = std::move(m_sendBuffers);
auto self(shared_from_this());
boost::asio::async_write(m_socket, *copy,
[self, copy](const boost::system::error_code &errorCode, size_t bytes_transferred) {
std::lock_guard<std::mutex> lock(self->m_sendMutex);
self->m_sending = false;
if (errorCode) {
// An error occurred
return;
}
self->DoSend();
});
}
}
//--------------------------------------------------------------------
void Connection::DoReceive() {
SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
auto self(shared_from_this()); // ***EXCEPTION HERE****
boost::asio::async_read_until(m_socket, m_receiveBuffer, '#',
[self](const boost::system::error_code &errorCode, size_t bytesRead) {
if (errorCode) {
// Notify our masters that we are ready to be destroyed
self->m_owner->OnConnectionClosed(self);
// An error occured
return;
}
// Grab the read data
std::istream stream(&self->m_receiveBuffer);
std::string data;
std::getline(stream, data, '#');
// Issue the next receive
if (!self->m_stopped) {
self->DoReceive();
}
});
}
//--------------------------------------------------------------------
//**ConnectionManager.cpp **
//#include "ConnectionManager.h"
//#include "Logger.h"
#include <boost/bind.hpp>
#include <system_error>
//------------------------------------------------------------------------------
ConnectionManager::ConnectionManager(unsigned port, size_t numThreads)
: m_io_service(), m_acceptor(m_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_listenSocket(m_io_service), m_threads(numThreads) {}
//------------------------------------------------------------------------------
ConnectionManager::~ConnectionManager() { Stop(); }
//------------------------------------------------------------------------------
void ConnectionManager::Start() {
if (m_io_service.stopped()) {
m_io_service.reset();
}
DoAccept();
for (auto &thread : m_threads) {
if (!thread.joinable()) {
thread = std::thread(&ConnectionManager::IoServiceThreadProc, this);
}
}
}
//------------------------------------------------------------------------------
void ConnectionManager::Stop() {
{
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.clear();
}
// TODO - Will the stopping of the io_service be enough to kill all the connections and ultimately have them get
// destroyed?
// Because remember they have outstanding ref count to thier shared_ptr in the async handlers
m_io_service.stop();
for (auto &thread : m_threads) {
if (thread.joinable()) {
thread.join();
}
}
}
//------------------------------------------------------------------------------
void ConnectionManager::IoServiceThreadProc() {
try {
// Log that we are starting the io_service thread
{
const std::string msg("io_service socket thread starting.");
SomeNamespace::Logger::GetInstance().Log(msg, __FILE__, __LINE__,
SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
}
// Run the asynchronous callbacks from the socket on this thread
// Until the io_service is stopped from another thread
m_io_service.run();
} catch (std::system_error &e) {
SomeNamespace::Logger::GetInstance().LogF("System error caught in io_service socket thread. Error Code: %d", e.code().value());
} catch (std::exception &e) {
SomeNamespace::Logger::GetInstance().LogF("Standard exception caught in io_service socket thread. Exception: %s", e.what());
} catch (...) {
SomeNamespace::Logger::GetInstance().LogF("Unhandled exception caught in io_service socket thread.");
}
SomeNamespace::Logger::GetInstance().LogF("io_service socket thread exiting.");
}
//------------------------------------------------------------------------------
void ConnectionManager::DoAccept() {
SomeNamespace::Logger::GetInstance().Log(__PRETTY_FUNCTION__, __FILE__, __LINE__, SomeNamespace::Logger::LOGGER_SEVERITY_INFO);
m_acceptor.async_accept(m_listenSocket, [this](const boost::system::error_code errorCode) {
if (errorCode) {
return;
}
{
// Create the connection from the connected socket
Connection::SharedPtr connection = Connection::Create(this, m_listenSocket);
{
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.push_back(connection);
connection->Start();
}
}
DoAccept();
});
}
//------------------------------------------------------------------------------
void ConnectionManager::OnConnectionClosed(Connection::SharedPtr connection) {
std::lock_guard<std::mutex> lock(m_connectionsMutex);
auto itConnection = std::find(m_connections.begin(), m_connections.end(), connection);
if (itConnection != m_connections.end()) {
m_connections.erase(itConnection);
}
}
//------------------------------------------------------------------------------
//**main.cpp**
//#include "ConnectionManager.h"
#include <cstring>
#include <iostream>
#include <string>
int main() {
ConnectionManager connectionManager(4000, 2);
connectionManager.Start();
std::this_thread::sleep_for(std::chrono::minutes(1));
connectionManager.Stop();
}

Operation Canceled on async_resolve

I have minor experience with c++ and facing some issue with boost-asio.
I want to rewrite standard boost-asio async-http-client example (http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/example/cpp03/http/client/async_client.cpp) in following way.
My goal is to have 2 classes;
AsyncHttpClient(that stores host and has member function that will send async calls to specified path).
AsyncHttpConnection (that takes io_service, host, path as parameters
and follows the flow specified in boost-asio async-http-client
example)
I have the following implementation
using boost::asio::ip::tcp;
class AsyncHttpConnection {
public:
AsyncHttpConnection(
boost::asio::io_service& io_service,
std::string host,
std::string path) : resolver_(io_service),
socket_(io_service),
host_(host),
path_(path)
{
tcp::resolver::query query(host_, "http");
resolver_.async_resolve(query,
boost::bind(&AsyncHttpConnection::handle_resolve,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
private:
std::string host_;
std::string path_;
tcp::resolver resolver_;
tcp::socket socket_;
boost::asio::streambuf request_;
boost::asio::streambuf response_;
void handle_resolve(
const boost::system::error_code& err,
tcp::resolver::iterator endpoint_iterator)
{
if (!err) {
// code here
} else {
std::cout << err.message() << std::endl; // GOT "Operation Canceled" here
}
}
// list of other handlers
};
class AsyncHttpClient {
public:
AsyncHttpClient(
boost::asio::io_service& io_service,
std::string host) : host_(host)
{
io_service_ = &io_service; // store address of io_service
}
void async_call(std::string path)
{
AsyncHttpConnection(*io_service_, host_, path);
}
private:
std::string host_;
boost::asio::io_service* io_service_; // pointer, because io_service is uncopyable;
};
int main(int argc, char* argv[])
{
boost::asio::io_service io_service;
AsyncHttpClient boost(io_service, "www.boost.org");
boost.async_call("/doc/libs/1_51_0/doc/html/boost_asio/example/http/client/async_client.cpp");
io_service.run();
}
I got an error "Operation Canceled" in this particular way;
If I instantiate AsyncHttpConnection in following way
int main(int argc, char* argv[])
{
boost::asio::io_service io_service;
AsyncHttpConnection(io_service, "www.boost.org", "path");
io_service.run();
}
I got everything working perfectly, I think the issue is using pointer to io_service. How can I solve this issue, if io_service object is uncopyable?
void async_call(std::string path) {
AsyncHttpConnection(*io_service_, host_, path);
}
The body constructs a temporary object of type AsyncHttpConnection. So, before the statement completes, the destructor for this type runs.
The default destructor does member-wise destruction. So it triggers the destructor tcp::resolver resolver_. The documentation for this class states that any pending asynchronous operation will be canceled on doing so.
In principle the "alternative" main has exactly the same problem (and indeed it fails with Operation canceled on my box). If it doesn't for you you're getting very fortunate timing of events.

cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>

boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>
it does not tell where is this error is occurring :|, here is my codes
AsyncConnection
#ifndef _ASYNCCONNECTION_H_
#define _ASYNCCONNECTION_H_
#include <boost\shared_ptr.hpp>
#include <boost\enable_shared_from_this.hpp>
#include <boost\bind.hpp>
#include <boost\asio.hpp>
class AsyncConnection : public boost::enable_shared_from_this<AsyncConnection>
{
public:
typedef boost::shared_ptr<AsyncConnection> Pointer;
explicit AsyncConnection(boost::asio::ip::tcp::socket& socket);
virtual ~AsyncConnection();
virtual void BeginReceive();
virtual void EndReceive(const boost::system::error_code& error, std::size_t bytes_transferred);
boost::asio::ip::tcp::socket& GetSocket();
private:
boost::asio::ip::tcp::socket m_socket;
char buffer[1024];
};
#endif
#include "AsyncConnection.h"
AsyncConnection::AsyncConnection(boost::asio::ip::tcp::socket& socket)
: m_socket(socket)
{
}
AsyncConnection::~AsyncConnection()
{
}
boost::asio::ip::tcp::socket& AsyncConnection::GetSocket(){
return m_socket;
}
void AsyncConnection::BeginReceive(){
boost::asio::async_read(socket, boost::asio::buffer(buffer),
boost::asio::transfer_at_least(1),
boost::bind(&AsyncConnection::EndReceive, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void AsyncConnection::EndReceive(const boost::system::error_code& error, std::size_t bytes_transferred){
if (!error){
}
else{
std::cout << error.message() << std::endl;
}
}
IAsyncConnectionFactory
#ifndef _IASYNCCONNECTIONFACTORY_H_
#define _IASYNCCONNECTIONFACTORY_H_
#include "AsyncConnection.h"
class IAsyncConnectionFactory
{
public:
IAsyncConnectionFactory();
virtual ~IAsyncConnectionFactory();
virtual AsyncConnection::Pointer Create(boost::asio::ip::tcp::socket& socket) = 0;
};
#endif
AsyncServer
#pragma once
#ifndef _ASYNCSERVER_H_
#define _ASYNCSERVER_H_
#include <string>
#include <boost/bind.hpp>
#include "IAsyncConnectionFactory.h"
using boost::asio::ip::tcp;
class AsyncServer
{
public:
AsyncServer(boost::asio::io_service& io_service, std::string ip, unsigned short port, boost::shared_ptr < IAsyncConnectionFactory> factory);
~AsyncServer();
void BeginAccept();
void EndAccept(AsyncConnection::Pointer connection, const boost::system::error_code& error);
private:
boost::shared_ptr < IAsyncConnectionFactory> m_factory;
tcp::acceptor acceptor;
};
#endif
#include "AsyncServer.h"
AsyncServer::AsyncServer(boost::asio::io_service& io_service, std::string ip, unsigned short port, boost::shared_ptr< IAsyncConnectionFactory> factory)
: acceptor(io_service, tcp::endpoint(boost::asio::ip::address_v4::from_string(ip), port)), m_factory(factory)
{
BeginAccept();
}
AsyncServer::~AsyncServer()
{
}
void AsyncServer::BeginAccept(){
AsyncConnection::Pointer new_connection = m_factory->Create(boost::asio::ip::tcp::socket(acceptor.get_io_service()));
acceptor.async_accept(new_connection->GetSocket(),
boost::bind(&AsyncServer::EndAccept, this, new_connection, boost::asio::placeholders::error));
}
void AsyncServer::EndAccept(AsyncConnection::Pointer connection, const boost::system::error_code& error){
if (!error)
{
connection->BeginReceive();
}
else
{
std::cout << error.message() << std::endl;
}
BeginAccept();
}
Your GetSocket member-function attempts to return tcp::socket by value. This is impossible, since tcp::socket cannot be copied.
You can return tcp::socket &, but make sure to avoid dangling references.
Update: if you still get this error, make sure that you never attempt to copy an object, which contains an Asio object. To get better diagnostics, make copy-ctor and operator = private in AsyncConnection and AsyncServer - then the compiler will point exactly to the place where such copying takes place.
Update2: I missed another point: your AsyncConnection constructor attempts to copy tcp::socket. Either store the socket by reference or use move semantics (in C++11).