Убедитесь, что продолжительность жизни объекта, содержащего outboundData
и outboundHeader
превышает объем async_write
операции.
Это делается в соответствующем server.cpp например, путем управления connection
через shared_ptr
и связывания shared_ptr
к проводнику. Вот соответствующие выдержки из кода:
/// Constructor opens the acceptor and starts waiting for the first incoming
/// connection.
server(...)
: acceptor_(...)
{
// 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)
{
// Successfully accepted a new connection. Send the list of stocks to the
// client. The connection::async_write() function will automatically
// serialize the data structure for us.
conn->async_write(...,
boost::bind(&server::handle_write, this,
boost::asio::placeholders::error, conn));
}
...
}
/// Handle completion of a write operation.
void handle_write(const boost::system::error_code& e, connection_ptr conn)
{
// Nothing to do. The socket will be closed automatically when the last
// reference to the connection object goes away.
}
connection
, который содержит outboundData
и outboundHeader
, создается и управляется с помощью shared_ptr
в server
конструктора. Затем shared_ptr
привязан к server::handle_accept()
, обработчику для async_accept
. В пределах server::handle_accept()
соединение привязано к server::handle_write()
, обработчику для connection::async_write()
. Хотя server::handle_write()
ничего не делает, он имеет решающее значение в цепочке, так как он сохраняет объект connection
через его связанный аргумент.
Можно утверждать, что это менее навязчивым, если connection
гарантирована его продолжительность жизни превысит async_write
операцию без наложения требования по вызывающему. Общим идиоматическим решением для этого является то, что connection
наследует от enable_shared_from_this
. Когда класс наследует от enable_shared_from_this
, он предоставляет функцию-член shared_from_this()
, которая возвращает действительный пример shared_ptr
в this
.
Вот пример, основанный на объектах server
и connection
в примере Boost.Asio serialization.
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
class connection
: public boost::enable_shared_from_this<connection>
{
public:
/// @brief Constructor.
connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
std::cout << "connection(): " << this << std::endl;
}
~connection()
{
std::cout << "~connection(): " << this << std::endl;
}
/// @brief Get the underlying socket. Used for making a connection
/// or for accepting an incoming connection.
boost::asio::ip::tcp::socket& socket()
{
return socket_;
}
/// @brief Asynchronously write data to the connection, invoking
/// handler upon completion or failure.
template <typename Handler>
void async_write(std::string data, Handler handler)
{
// Perform processing on data and copy to member variables.
using std::swap;
swap(data_, data);
// Create a buffer sequence.
boost::array<boost::asio::const_buffer, 1> buffers = {{
boost::asio::buffer(data_)
}};
std::cout << "connection::async_write() " << this << std::endl;
// Write to the socket.
boost::asio::async_write(
socket_,
buffers, // Buffer sequence copied, not the underlying buffers.
boost::bind(&connection::handle_write<Handler>,
shared_from_this(), // Keep connection alive throughout operation.
boost::asio::placeholders::error,
handler));
}
private:
/// @brief Invokes user provided handler. This member function
/// allows for the connection object's lifespan to be
/// extended during the binding process.
template <typename Handler>
void handle_write(const boost::system::error_code& error,
Handler handler)
{
std::cout << "connection::handle_write() " << this << std::endl;
handler(error);
}
private:
boost::asio::ip::tcp::socket socket_;
std::string data_;
};
class server
{
public:
/// @brief Constructor opens an acceptor, waiting for 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))
{
start_accept();
}
private:
/// @brief Start an accept operation for a new connection.
void start_accept()
{
boost::shared_ptr<connection> new_conn =
boost::make_shared<connection>(
boost::ref(acceptor_.get_io_service()));
acceptor_.async_accept(new_conn->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error, new_conn));
}
/// @brief Handle completion of a accept operation.
void handle_accept(const boost::system::error_code& error,
boost::shared_ptr<connection> conn)
{
if (!error)
{
// Successfully accepted a new connection. Write data to it.
conn->async_write("test data",
boost::protect(
boost::bind(&server::handle_write, this,
boost::asio::placeholders::error)));
}
// Start accepting another connection.
start_accept();
}
void handle_write(const boost::system::error_code& error)
{
std::cout << "server::handle_write()" << std::endl;
}
private:
/// The acceptor object used to accept incoming socket connections.
boost::asio::ip::tcp::acceptor acceptor_;
};
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;
server server(io_service, port);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
Запуск программы, и подключение от другого терминала в результате следующий вывод:
connection(): 0x8cac18c
connection::async_write() 0x8cac18c
connection(): 0x8cac1e4
connection::handle_write() 0x8cac18c
server::handle_write()
~connection(): 0x8cac18c
Обратите внимание, как продолжительность жизни в connection
объекта распространяется, по меньшей мере, что в async_write
операции. Измененный API позволяет server
не управлять connection
, так как объект будет управлять собой. Обратите внимание, что boost::protect
требуется из-за вложенного boost::bind
. Существуют альтернативы этому, которые не помещают нагрузку на вызывающего абонента, например, упаковывают обработчик привязки в кортеж, как это сделано в connection::async_read()
в примере Boost.Asio.
Хотя ваш полный пример, основанный на примере сериализации, отобрал сериализацию и отправил только строку «тестовые данные». Это помогло мне исправить мою проблему. Я думаю, что мой объект, содержащий Connection, удалялся раньше, и shared_ptr, похоже, исправил это – Shredder2500