Документация Boost.Asio гласит:
Буферный объект представляет непрерывную область памяти в виде 2-х кортежей, состоящих из указателя и размера в байтах. Кортеж в форме {void *, size_t} указывает изменяемый (модифицируемый) участок памяти.
Это означает, что для вызова async_read
для записи данных в буфер он должен быть (в базовом объекте буфера) непрерывным блоком памяти. Кроме того, объект буфера должен иметь возможность записи в этот блок памяти.
std::string
не разрешает произвольные записи в его буфер, поэтому async_read
не может записывать куски памяти в строковый буфер (обратите внимание, что std::string
делает предоставляя вызывающей стороне доступ только для чтения к базовый буфер с помощью метода data()
, который гарантирует, что возвращаемый указатель будет действителен до следующего вызова неконстантной функции-члена. По этой причине Asio может легко создать const_buffer
, обертывающий std::string
, и вы можно использовать с async_write
).
В документации Asio приведен пример кода для простой программы "chat" (см. http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat), в которой есть хороший способ преодоления этой проблемы. По сути, вам нужно иметь отправляющую отправку TCP по размеру сообщения). во-первых, в своего рода «заголовке», и ваш обработчик чтения должен интерпретировать заголовок, чтобы выделить буфер фиксированного размера, подходящий для чтения фактических данных.
Что касается необходимости использования shared_from_this()
в async_read
и async_write
, причина в том, что он гарантирует, что метод, заключенный в boost::bind
, всегда будет ссылаться на живой объект. Рассмотрим следующую ситуацию:
- Ваш
handle_accept
метод вызывает async_read
и отправляет обработчик «в реактор» - в основном вы просили io_service
вызвать Connection::handle_user_read
, когда он заканчивает чтение данных из сокета. io_service
сохраняет этот функтор и продолжает цикл, ожидая завершения операции асинхронного чтения.
- После вашего вызова
async_read
объект Connection
по какой-то причине освобождается (завершение программы, состояние ошибки и т. Д.)
- Предположим,
io_service
теперь определяет, что асинхронное чтение завершено, после Connection
объект был освобожден, но до io_service
уничтожен (это может произойти Например, если io_service::run
работает в отдельном потоке, как обычно). Теперь io_service
пытается вызвать обработчик, и у него есть недопустимая ссылка на объект Connection
.
Решение состоит в том, чтобы выделить Connection
через shared_ptr
и использовать shared_from_this()
вместо this
при отправке обработчика «в реактор» - это позволяет io_service
хранить общую ссылку на объект, и shared_ptr
гарантирует, что он не будет освобожден до истечения срока действия последней ссылки.
Итак, ваш код должен выглядеть примерно так:
class Connection : public boost::enable_shared_from_this<Connection>
{
public:
Connection(tcp::acceptor &acceptor) :
acceptor_(acceptor),
socket_(acceptor.get_io_service(), tcp::v4())
{ }
void start()
{
acceptor_.get_io_service().post(
boost::bind(&Connection::start_accept, shared_from_this()));
}
private:
void start_accept()
{
acceptor_.async_accept(socket_,
boost::bind(&Connection::handle_accept, shared_from_this(),
placeholders::error));
}
void handle_accept(const boost::system::error_code& err)
{
if (err)
{
disconnect();
}
else
{
async_read(socket_, boost::asio::buffer(user_),
boost::bind(&Connection::handle_user_read, shared_from_this(),
placeholders::error, placeholders::bytes_transferred));
}
}
//...
};
Обратите внимание, что теперь вы должны убедиться, что каждый Connection
объект выделен через shared_ptr
, например ::1010 *
boost::shared_ptr<Connection> new_conn(new Connection(...));
Надеюсь, это поможет!