Boost.Asio async_write and strands - c++

I 'm using a strand to avoid concurrent writes on TCP server using Boost.Asio. But it seems it only prevents concurrent execution of handlers.
Indeed if I do two successive async_write, one with a very big packet, and the other with a very small one, wireshark shows interleaves. As async_write is composed of multiple calls of async_write_some, it seems that the handler of my second write is allowed to be executed between two handlers of the first call. Which is very bad for me.
Wireshark output: [Packet 1.1] [Packet 1.2] [Packet 2] [Packet 1.3] ... [Packet 1.x]
struct Command
{
// Header
uint64_t ticket_id; // UUID
uint32_t data_size; // size of data
// data
std::vector<unsigned char> m_internal_buffer;
}
typedef std::shared_ptr<Command> command_type;
void tcp_server::write(command_type cmd)
{
boost::asio::async_write(m_socket, boost::asio::buffer(cmd->getData(), cmd->getTotalPacketSize()),
boost::asio::bind_executor(m_write_strand,
[this, cmd](const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error)
{
// report
}
}
)
);
}
and the main:
int main()
{
tcp_server.write(big_packet); // Packet 1 = 10 MBytes !
tcp_server.write(small_packet); // Packet 2 = 64 kbytes
}
Is the strand not appropriate in my case ?
P.S: I saw that close topic here but it does not cover the same use case in my opinion.

You have to make sure your async operation is initiated from the strand. Your code currently doesn't show this to be the case. Hopefully this helps, otherwise, post a MCVE
So e.g.
void tcp_server::write(command_type cmd)
{
post(m_write_strand, [this, cmd] { this->do_write(cmd); });
}
Making up a MCVE from your question code:
Live On Coliru
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using Executor = boost::asio::thread_pool::executor_type;
struct command {
char const* getData() const { return ""; }
size_t getTotalPacketSize() const { return 1; }
};
using command_type = command*;
struct tcp_server {
tcp_server(Executor ex) : m_socket(ex), m_write_strand(ex)
{
// more?
}
void write(command_type cmd);
void do_write(command_type cmd);
tcp::socket m_socket;
boost::asio::strand<Executor> m_write_strand;
};
void tcp_server::write(command_type cmd)
{
post(m_write_strand, [this, cmd] { this->do_write(cmd); });
}
void tcp_server::do_write(command_type cmd)
{
boost::asio::async_write(
m_socket,
boost::asio::buffer(cmd->getData(), cmd->getTotalPacketSize()),
bind_executor(m_write_strand,
[/*this, cmd*/](boost::system::error_code error,
size_t bytes_transferred) {
if (error) {
// report
}
}));
}
int main() {
boost::asio::thread_pool ioc;
tcp_server tcp_server(ioc.get_executor());
command_type big_packet{}, small_packet{};
tcp_server.write(big_packet); // Packet 1 = 10 MBytes !
tcp_server.write(small_packet); // Packet 2 = 64 kbytes
ioc.join();
}

Related

Why doesn't boost::asio::ip::udp::socket::receive_from throw interruption exception in Windows?

volatile std::sig_atomic_t running = true;
int main()
{
boost::asio::thread_pool tpool;
boost::asio::signal_set signals(tpool, SIGINT, SIGTERM);
signals.async_wait([](auto && err, int) { if (!err) running = false; });
while(running)
{
std::array<std::uint8_t, 1024> data;
socket.recieve_from(boost::asio::buffer(data), ....); // (1)
// calc(data);
}
return 0;
}
If my code is blocked in the (1) line in Linux and I try raise the signal, for example, with htop then the line (1) throws exception about the interruption but in Windows it doesn't. The problem in what I don't know how to exit the application.
What needs to do my program works equally in both OSs? Thanks.
Use Windows 10 (msvc 17), Debian 11 (gcc-9), Boost 1.78.
Regardless of the question how you "raise the signal" on Windows there's the basic problem that you're relying on OS specifics to cancel a synchronous operation.
Cancellation is an ASIO feature, but only for asynchronous operations. So, consider:
signals.async_wait([&socket](auto&& err, int) {
if (!err) {
socket.cancel();
}
});
Simplifying without a thread_pool gives e.g.:
Live On Coliru
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING 1
#include <boost/asio.hpp>
namespace asio = boost::asio;
using asio::ip::udp;
using boost::system::error_code;
struct Program {
Program(asio::any_io_executor executor)
: signals_{executor, SIGINT, SIGTERM}
, socket_{executor} //
{
signals_.async_wait([this](error_code ec, int) {
if (!ec) {
socket_.cancel();
}
});
socket_.open(udp::v4());
socket_.bind({{}, 4444});
receive_loop();
}
private:
asio::signal_set signals_;
udp::socket socket_;
std::array<std::uint8_t, 1024> data_;
udp::endpoint ep_;
void receive_loop() {
socket_.async_receive_from( //
asio::buffer(data_), ep_, [this](error_code ec, size_t) {
if (!ec)
receive_loop();
});
}
};
int main() {
asio::io_context ioc;
Program app(ioc.get_executor());
using namespace std::chrono_literals;
ioc.run_for(10s); // for COLIRU
}
Prints (on coliru):
#asio|1663593973.457548|0*1|signal_set#0x7ffe0b639998.async_wait
#asio|1663593973.457687|0*2|socket#0x7ffe0b6399f0.async_receive_from
#asio|1663593973.457700|.2|non_blocking_recvfrom,ec=system:11,bytes_transferred=0
#asio|1663593974.467205|.2|non_blocking_recvfrom,ec=system:0,bytes_transferred=13
#asio|1663593974.467252|>2|ec=system:0,bytes_transferred=13
#asio|1663593974.467265|2*3|socket#0x7ffe0b6399f0.async_receive_from
#asio|1663593974.467279|.3|non_blocking_recvfrom,ec=system:11,bytes_transferred=0
#asio|1663593974.467291|<2|
#asio|1663593975.481800|.3|non_blocking_recvfrom,ec=system:0,bytes_transferred=13
#asio|1663593975.481842|>3|ec=system:0,bytes_transferred=13
#asio|1663593975.481854|3*4|socket#0x7ffe0b6399f0.async_receive_from
#asio|1663593975.481868|.4|non_blocking_recvfrom,ec=system:11,bytes_transferred=0
#asio|1663593975.481878|<3|
#asio|1663593976.494097|.4|non_blocking_recvfrom,ec=system:0,bytes_transferred=13
#asio|1663593976.494138|>4|ec=system:0,bytes_transferred=13
#asio|1663593976.494150|4*5|socket#0x7ffe0b6399f0.async_receive_from
#asio|1663593976.494164|.5|non_blocking_recvfrom,ec=system:11,bytes_transferred=0
#asio|1663593976.494176|<4|
#asio|1663593976.495085|>1|ec=system:0,signal_number=2
#asio|1663593976.495119|1|socket#0x7ffe0b6399f0.cancel
#asio|1663593976.495129|<1|
#asio|1663593976.495151|>5|ec=system:125,bytes_transferred=0
#asio|1663593976.495162|<5|
#asio|1663593976.495184|0|socket#0x7ffe0b6399f0.close
#asio|1663593976.495244|0|signal_set#0x7ffe0b639998.cancel
So that's 3 successful receives, followed by a signal 2 (INT) and cancellation which results in ec=125 (asio::error:operation_aborted) and shutdown.
Multi-threading
There's likely no gain for using multiple threads, but if you do, use a strand to synchronize access to the IO objects:
asio::thread_pool ioc;
Program app(make_strand(ioc));

QUdpSocket client connected to echo server not working

I am developing a QT 6 Widget based UDP audio application that repeatedly sends out a single UDP audio frame sample (4K bytes sine wave tone) to a remote UDP echo server at a predetermined rate - (right now the echo server is hosted locally though).
The UDP echo server is based on the asynchronous UDP echo server sample developed by the asio author (not me). This is shown below (slightly modified to include a hard coded 4K block for testing purposes). The application is also launched with a port parameter 1234 - so it listens on port 1234 for the incoming audio packet that it will echo back to client.
//
// async_udp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <iostream>
#include <asio/ts/buffer.hpp>
#include <asio/ts/internet.hpp>
using asio::ip::udp;
class server {
public:
server(asio::io_context& io_context, short port)
: socket_(io_context, udp::endpoint(udp::v4(), port)) {
do_receive();
}
void do_receive() {
socket_.async_receive_from(
asio::buffer(data_, max_length), sender_endpoint_,
[this](std::error_code ec, std::size_t bytes_recvd) {
if (!ec && bytes_recvd > 0) {
do_send(bytes_recvd);
} else {
do_receive();
}
});
}
void do_send(std::size_t length) {
socket_.async_send_to(
asio::buffer(data_, length), sender_endpoint_,
[this](std::error_code /*ec*/, std::size_t /*bytes_sent*/) {
do_receive();
});
}
private:
udp::socket socket_;
udp::endpoint sender_endpoint_;
enum { max_length = 4096 };
char data_[max_length]{};
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: async_udp_echo_server <port>\n";
return 1;
}
asio::io_context io_context;
server s(io_context, std::atoi(argv[1]));
io_context.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
I currently have this working successfully in the client as a stand alone asio worker thread, however since I need to graphically display the returned audio packets, I cannot use the stand alone asio thread approach; I need to use QT with its signals/slots async magic instead.
For the purposes of illustration, I also include my working asio client code that runs in a separate joinable thread. This client thread uses a asio::steady_timer that fires an asynchronous 4k UDP packet repeatedly to the echo server. The code also compares the echoed back contents to this outgoing audio sample successfully.
void
RTPClient::start() {
mpSendEndpoint = std::make_unique<ip::udp::endpoint>(
ip::address::from_string(mConfig.mHostName),
mConfig.mPortNum);
mpSocket = std::make_unique<ip::udp::socket>(
mIOContext, mpSendEndpoint->protocol());
mpSocketTimer = std::make_unique<steady_timer>(
mIOContext);
mWorker = std::thread([this]() {
mIOContext.run();
});
if (!mShutdownFlag) {
// kick off the async chain by immediate timeout
mpSocketTimer->expires_after(std::chrono::seconds(0));
mpSocketTimer->async_wait([this]<typename T0>(T0&& ec) {
handle_timeout(std::forward<T0>(ec));
});
}
}
void
RTPClient::handle_timeout(const error_code& ec)
{
if (!ec && !mShutdownFlag) {
if (!mpAudioOutput) {
// check to see if there is new audio test data waiting in queue
if (const auto audioData = mIPCQueue->try_pop(); audioData) {
// new audio waiting, copy the data to mpAudioTXData and allocate an identically
// sized receive buffer to receive the echo replies from the server
mpAudioInput = std::make_unique<AudioDatagram>(audioData->first.size());
mpAudioOutput = std::make_unique<AudioDatagram>(std::move(audioData->first));
mAudioBlockUSecs = audioData->second;
} else {
mpSocketTimer->expires_after(seconds(1));
mpSocketTimer->async_wait([this]<typename T0>(T0&& ec) {
handle_timeout(std::forward<T0>(ec));
});
// nothing to send as waveform data not received from GUI.
// short circuit return with a 1 sec poll
return;
}
}
mpSocket->async_send_to(asio::buffer(
mpAudioOutput.get(), mpAudioOutput->size()),
*mpSendEndpoint, [this]<typename T0, typename T1>(T0&& ec, T1&& bytes_transferred) {
handle_send_to(std::forward<T0>(ec), std::forward<T1>(bytes_transferred));
});
}
}
void
RTPClient::handle_send_to(const error_code& ec, std::size_t bytes_transferred) {
if (!ec && bytes_transferred > 0 && !mShutdownFlag) {
mpSocketTimer->expires_after(microseconds(mAudioBlockUSecs));
mpSocketTimer->async_wait([this]<typename T0>(T0&& ec) {
handle_timeout(std::forward<T0>(ec));
});
mpSocket->async_receive_from(asio::buffer(
mpAudioInput.get(), mpAudioInput->size()), *mpSendEndpoint,
[this]<typename T0, typename T1>(T0&& ec, T1&& bytes_transferred) {
handle_receive(std::forward<T0>(ec), std::forward<T1>(bytes_transferred));
});
}
}
void
RTPClient::handle_receive(const error_code& ec, std::size_t bytes_transferred) {
if (!ec && bytes_transferred > 0) {
double foo = 0.0;
for (const auto next : *mpAudioOutput) {
foo += (double)next;
}
double bar = 0.0;
for (const auto next : *mpAudioInput) {
bar += (double)next;
}
if (foo != bar)
{
auto baz = 0;
(void)baz;
}
}
}
/**
* Shutdown the protocol instance by shutting down the IPC
* queue and closing the socket and associated timers etc.
*
* <p>This is achieved by setting a flag which is read by the
* busy loop as an exit condition.
*/
void
RTPClient::shutdown() {
// set the shared shutdown flag
mShutdownFlag = true;
// wake up any locked threads so they can see the above flag
if (mIPCQueue) {
mIPCQueue->shutdown();
}
// stop the socket timer - do not reset it
// as there are some time sensitive parts in the code
// where mpSocketTimer is dereferenced
if (mpSocketTimer) {
mpSocketTimer->cancel();
}
std::error_code ignoredError;
// close the socket if we created & opened it, making
// sure that we close down both ends of the socket.
if (mpSocket && mpSocket->is_open()) {
mpSocket->shutdown(ip::udp::socket::shutdown_both, ignoredError);
// reset so we will reallocate and then reopen
// via boost::async_connect(...) later.
mpSocket.reset();
}
// wait for the any other detached threads to see mShutdownFlag
// as it is running in a detached mWorkerThread which sleeps
// for 50ms CDU key polling requests.
std::this_thread::sleep_for(milliseconds(200));
}
I need to replace this separate asio client thread code with a QUdpSocket based client code to do the equivalent, as I need to use signals/slots to notify the GUI when the blocks arrive and display the returned waveform in a widget. To this end I have the following QT worker thread. I can see that the asio echo server receives the datagram, however I do not know how to receive the echoed contents back into the client. Is there some bind or connect call that I need to do on the client side. I am totally confused with when to call bind and when to call connect on UDP sockets.
// SYSTEM INCLUDES
//#include <..>
// APPLICATION INCLUDES
#include "RTPSession.h"
// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// FUNCTIONS
// NAMESPACE USAGE
using namespace std::chrono;
// STATIC VARIABLE INITIALIZATIONS
std::mutex RTPSession::gMutexGuard;
RTPSession::RTPSession(QObject* parent)
: QObject(parent)
, mpSocket{ std::make_unique<QUdpSocket>(parent) }
{
mpSocket->bind(45454, QUdpSocket::DefaultForPlatform);
connect(mpSocket.get(), &QUdpSocket::readyRead,
this, &RTPSession::processPendingDatagrams);
}
/**
* Thread function that listens RTP session updates.
*
* <p>The implementation polls for shutdown every second.
*
* #param rRTPInfo [in] qt thread parameters.
*/
void
RTPSession::doWork(
const std::tuple<int32_t, int32_t, int32_t>& /*rRTPInfo*/)
{
try {
// just dispatched, so reset exit flag
mExitWorkLoop = false;
int frameCounter = 0;
while (!mExitWorkLoop) {
constexpr auto gPollMillis = 1000;
// poll using shortest (non zero) interval in schedule
std::unique_lock<std::mutex> lk(gMutexGuard);
mCondVariable.wait_for(lk, milliseconds(gPollMillis),
[this] { return mExitWorkLoop; });
QByteArray datagram = "Broadcast message " + QByteArray::number(frameCounter++);
mpSocket->writeDatagram(datagram.data(), datagram.size(),
QHostAddress::LocalHost, 1234);
if (mpSocket->hasPendingDatagrams()) {
//mpSocket->readDatagram()
int t = 0;
(void)t;
}
// update GUI with the audio stats - add more later
emit updateProgress(frameCounter++);
}
} catch (const std::exception& rEx) {
// exit thread with the exception details
emit finishWork(tr("exiting worker, error:") + rEx.what());
}
// exit thread with status bar message
emit finishWork(tr("finished"));
}
void
RTPSession::shutdown()
{
// Critical section.
std::scoped_lock<std::mutex> lock(gMutexGuard);
mExitWorkLoop = true;
// Notify the potentially sleeping thread that is
// waiting for up to 1 second
mCondVariable.notify_one();
}
void
RTPSession::processPendingDatagrams() {
QByteArray datagram;
while (mpSocket->hasPendingDatagrams()) {
datagram.resize(int(mpSocket->pendingDatagramSize()));
mpSocket->readDatagram(datagram.data(), datagram.size());
//statusLabel->setText(tr("Received datagram: \"%1\"")
// .arg(datagram.constData()));
}
}

Memory Leak at async_handshake using boost beast and openssl

I'm using OpenSSL 1.1.1b and Boost 1.68 to create a simple server using https.
I followed the examples provided by boost beast and in particular the advance server flex.
The application seems to work properly. I can accept https session and also wss sessions.
The problem is when I exit from the application where the Visual Leak Detector finds 16 memory leaks that target at:
c:\openssl-1.1.1b\crypto\mem.c (233): abc.exe!CRYPTO_zalloc
c:\openssl-1.1.1b\crypto\err\err.c (716): abc.exe!ERR_get_state + 0x17 bytes
c:\openssl-1.1.1b\crypto\err\err.c (443): abc.exe!ERR_clear_error + 0x5 bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\impl\engine.ipp (235): abc.exe!boost::asio::ssl::detail::engine::perform
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\impl\engine.ipp (137): abc.exe!boost::asio::ssl::detail::engine::handshake
I modified the pattern of the http session from the original boost beast code but it should perform exactly the same things.
I've tried to understand if the memory leaks increase based on the number of connections but it seems not. I don't understand how to get rid of this problem.
Following the code I used.
First a based http session class
class CApplicationServerBaseHttpSession
{
public:
std::shared_ptr<CApplicationServerSharedState> m_state = nullptr;
CApplicationServerHttpQueue m_queue;
// The parser is stored in an optional container so we can
// construct it from scratch it at the beginning of each new message.
boost::optional<boost::beast::http::request_parser<boost::beast::http::string_body>> parser_;
protected:
boost::asio::steady_timer m_timer;
boost::beast::flat_buffer buffer_;
boost::log::sources::severity_channel_logger<boost::log::trivial::severity_level> m_Logger{boost::log::keywords::channel = LOG_APPLICATION_SERVER_CHANNEL_ID};
boost::asio::strand<boost::asio::io_context::executor_type> m_strand;
public:
// Construct the session
CApplicationServerBaseHttpSession(
boost::asio::io_context& ioc,
boost::beast::flat_buffer buffer,
std::shared_ptr<CApplicationServerSharedState> const& state)
: m_state(state)
, m_strand(ioc.get_executor())
, m_timer(ioc,
(std::chrono::steady_clock::time_point::max)()
)
, m_queue(*this)
, buffer_(std::move(buffer))
{
}
void DoRead();
void OnRead(boost::system::error_code ec);
void OnWrite(boost::system::error_code ec, bool close);
virtual void WriteRequestStringBody(boost::beast::http::response<boost::beast::http::string_body> & msg) = 0;
virtual void WriteRequestFileBody(boost::beast::http::response<boost::beast::http::file_body> & msg) = 0;
protected:
virtual void ReadRequest() = 0;
virtual void DoEof() = 0;
virtual std::string GetRemoteAddress() = 0;
virtual void MakeWebSocketSession(boost::beast::http::request<boost::beast::http::string_body> req) = 0;
};
Here the implementation:
void CApplicationServerBaseHttpSession::DoRead()
{
// Set the timer
m_timer.expires_after(std::chrono::seconds(OCV_HTTP_SESSION_TIMER_EXPIRE_AFTER));
// Construct a new parser for each message
parser_.emplace();
// Apply a reasonable limit to the allowed size
// of the body in bytes to prevent abuse.
parser_->body_limit(HTTP_BODY_LIMIT);
this->ReadRequest();
}
void CApplicationServerBaseHttpSession::OnRead(boost::system::error_code ec)
{
// Happens when the timer closes the socket
if(ec == boost::asio::error::operation_aborted)
return;
// This means they closed the connection
if(ec == http::error::end_of_stream)
return this->DoEof();
if(ec == boost::asio::ssl::error::stream_truncated){
// "stream truncated" means that the other end closed the connection abruptly.
return warning(ec, "Http read", m_Logger);
}
if(ec)
return fail(ec, "Http read", m_Logger);
// See if it is a WebSocket Upgrade
if(websocket::is_upgrade(parser_->get())) {
// Get a websocket request handler to execute operation as authentication and authorization
// If these steps are allowed than the websocket session will be started
std::shared_ptr<CApplicationServerWsApiBase> endpointWs = m_state->GetEndpointWs(parser_->get().target().to_string());
if(endpointWs) {
int endpointErrorDefault = endpointWs->HandleRequest(parser_->get());
if(endpointErrorDefault > 0) { // Success Auth
// Make timer expire immediately, by setting expiry to time_point::min we can detect
// the upgrade to websocket in the timer handler
m_timer.expires_at((std::chrono::steady_clock::time_point::min)());
// Transfer the stream to a new WebSocket session
return MakeWebSocketSession(parser_->release());
} else {
// Authentication or Authorization failed
m_queue(endpointWs->GetResponseError(parser_->get(), endpointErrorDefault));
return;
}
} else {
// Wrong endpoint called: BadRequest
std::shared_ptr<CApplicationServerApiBase> endpoint = m_state->GetEndpoint(ApiURI::REQUEST_NOT_IMPLEMENTED);
if(endpoint) {
endpoint->HandleRequest(m_state->GetDocRoot(), parser_->release(), m_queue);
}
return;
}
}
BOOST_LOG_SEV(m_Logger, boost::log::trivial::trace) <<
"Request From: " <<
this->GetRemoteAddress() <<
" Request Target: " <<
parser_->get().target().to_string();
std::shared_ptr<CApplicationServerApiBase> endpoint = m_state->GetEndpoint(parser_->get().target().to_string());
if(endpoint) {
endpoint->HandleRequest(m_state->GetDocRoot(), parser_->release(), m_queue);
}
// If we aren't at the queue limit, try to pipeline another request
if(!m_queue.IsFull()) {
DoRead();
}
}
void CApplicationServerBaseHttpSession::OnWrite(boost::system::error_code ec, bool close)
{
// Happens when the timer closes the socket
if(ec == boost::asio::error::operation_aborted)
return;
if(ec)
return fail(ec, "write", m_Logger);
if(close) {
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return this->DoEof();
}
// Inform the queue that a write completed
if(m_queue.OnWrite()) {
// Read another request
DoRead();
}
}
The https session:
class COcvApplicationServerHttpSessionSSL
: public std::enable_shared_from_this<COcvApplicationServerHttpSessionSSL>
, public CApplicationServerBaseHttpSession
{
public:
public:
COcvApplicationServerHttpSessionSSL(boost::asio::ip::tcp::socket&& socket,boost::asio::ssl::context& ctx, boost::beast::flat_buffer&& buffer, std::shared_ptr<CApplicationServerSharedState> const& state);
~COcvApplicationServerHttpSessionSSL();
// Called by the base class
boost::beast::ssl_stream<boost::asio::ip::tcp::socket>& Stream();
boost::beast::ssl_stream<boost::asio::ip::tcp::socket> ReleaseStream();
void DoTimeout();
// Start the asynchronous operation
void Run();
void OnHandshake(boost::system::error_code ec, std::size_t bytes_used);
void OnShutdown(boost::system::error_code ec);
void OnTimer(boost::system::error_code ec);
private:
public:
boost::beast::ssl_stream<boost::asio::ip::tcp::socket> m_stream;
bool m_eof = false;
protected:
// Inherited via COcvApplicationServerBaseHttpSession
virtual void ReadRequest() override;
virtual void WriteRequestStringBody(boost::beast::http::response<boost::beast::http::string_body> & msg) override;
virtual void WriteRequestFileBody(boost::beast::http::response<boost::beast::http::file_body> & msg) override;
virtual void DoEof() override;
virtual std::string GetRemoteAddress() override;
virtual void MakeWebSocketSession(boost::beast::http::request<boost::beast::http::string_body> req) override;
};
and at the end the implementatition
COcvApplicationServerHttpSessionSSL::COcvApplicationServerHttpSessionSSL(tcp::socket&& socket, ssl::context & ctx, beast::flat_buffer&& buffer, std::shared_ptr<CApplicationServerSharedState> const & state)
: CApplicationServerBaseHttpSession(
socket.get_executor().context(),
std::move(buffer),
state)
, m_stream(std::move(socket), ctx)
{
}
COcvApplicationServerHttpSessionSSL::~COcvApplicationServerHttpSessionSSL()
{
}
beast::ssl_stream<tcp::socket> & COcvApplicationServerHttpSessionSSL::Stream()
{
return m_stream;
}
beast::ssl_stream<tcp::socket> COcvApplicationServerHttpSessionSSL::ReleaseStream()
{
return std::move(m_stream);
}
void COcvApplicationServerHttpSessionSSL::DoTimeout()
{
// If this is true it means we timed out performing the shutdown
if(m_eof)
return;
// Start the timer again
m_timer.expires_at(
(std::chrono::steady_clock::time_point::max)());
OnTimer({});
DoEof();
}
std::string COcvApplicationServerHttpSessionSSL::GetRemoteAddress()
{
return Stream().next_layer().remote_endpoint().address().to_string();
}
void COcvApplicationServerHttpSessionSSL::MakeWebSocketSession(boost::beast::http::request<boost::beast::http::string_body> req)
{
std::make_shared<CApplicationServerWebSocketSessionSSL>(
std::move(m_stream), m_state)->Run(std::move(req));
}
void COcvApplicationServerHttpSessionSSL::Run()
{
// Make sure we run on the strand
if(!m_strand.running_in_this_thread())
return boost::asio::post(
boost::asio::bind_executor(
m_strand,
std::bind(
&COcvApplicationServerHttpSessionSSL::Run,
shared_from_this())));
// Run the timer. The timer is operated
// continuously, this simplifies the code.
OnTimer({});
// Set the timer
m_timer.expires_after(std::chrono::seconds(OCV_HTTP_SESSION_TIMER_EXPIRE_AFTER));
// Perform the SSL handshake
// Note, this is the buffered version of the handshake.
m_stream.async_handshake(
ssl::stream_base::server,
buffer_.data(),
boost::asio::bind_executor(
m_strand,
std::bind(
&COcvApplicationServerHttpSessionSSL::OnHandshake,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2)));
}
void COcvApplicationServerHttpSessionSSL::OnHandshake(boost::system::error_code ec, std::size_t bytes_used)
{
// Happens when the handshake times out
if(ec == boost::asio::error::operation_aborted)
return;
if(ec)
return fail(ec, "handshake", m_Logger);
// Consume the portion of the buffer used by the handshake
buffer_.consume(bytes_used);
DoRead();
}
void COcvApplicationServerHttpSessionSSL::OnShutdown(boost::system::error_code ec)
{
// Happens when the shutdown times out
if(ec == boost::asio::error::operation_aborted || ec == boost::asio::ssl::error::stream_truncated)
return;
if(ec)
return fail(ec, "shutdown HTTPS", m_Logger);
// At this point the connection is closed gracefully
}
void COcvApplicationServerHttpSessionSSL::OnTimer(boost::system::error_code ec)
{
if(ec && ec != boost::asio::error::operation_aborted)
return fail(ec, "timer", m_Logger);
// Check if this has been upgraded to Websocket
if(m_timer.expires_at() == (std::chrono::steady_clock::time_point::min)())
return;
// Verify that the timer really expired since the deadline may have moved.
if(m_timer.expiry() <= std::chrono::steady_clock::now())
return DoTimeout();
// Wait on the timer
m_timer.async_wait(
boost::asio::bind_executor(
m_strand,
std::bind(
&COcvApplicationServerHttpSessionSSL::OnTimer,
shared_from_this(),
std::placeholders::_1)));
}
void COcvApplicationServerHttpSessionSSL::ReadRequest()
{
// Read a request
http::async_read(
Stream(),
buffer_,
*parser_,
boost::asio::bind_executor(
m_strand,
std::bind(
&CApplicationServerBaseHttpSession::OnRead,
shared_from_this(),
std::placeholders::_1)));
}
void COcvApplicationServerHttpSessionSSL::WriteRequestStringBody(boost::beast::http::response<boost::beast::http::string_body> & msg)
{
boost::beast::http::async_write(
Stream(),
msg,
boost::asio::bind_executor(
m_strand,
std::bind(
&CApplicationServerBaseHttpSession::OnWrite,
shared_from_this(),
std::placeholders::_1,
msg.need_eof()
)
)
);
}
void COcvApplicationServerHttpSessionSSL::WriteRequestFileBody(boost::beast::http::response<boost::beast::http::file_body> & msg)
{
boost::beast::http::async_write(
Stream(),
msg,
boost::asio::bind_executor(
m_strand,
std::bind(
&CApplicationServerBaseHttpSession::OnWrite,
shared_from_this(),
std::placeholders::_1,
msg.need_eof()
)
)
);
}
void COcvApplicationServerHttpSessionSSL::DoEof()
{
m_eof = true;
// Set the timer
m_timer.expires_after(std::chrono::seconds(OCV_HTTP_SESSION_TIMER_EXPIRE_DO_EOF));
// Perform the SSL shutdown
m_stream.async_shutdown(
boost::asio::bind_executor(
m_strand,
std::bind(
&COcvApplicationServerHttpSessionSSL::OnShutdown,
shared_from_this(),
std::placeholders::_1)));
}
The Visual Leak Detector gives me the following:
c:\openssl-1.1.1b\crypto\mem.c (233): abc.exe!CRYPTO_zalloc
c:\openssl-1.1.1b\crypto\err\err.c (716): abc.exe!ERR_get_state + 0x17 bytes
c:\openssl-1.1.1b\crypto\err\err.c (443): abc.exe!ERR_clear_error + 0x5 bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\impl\engine.ipp (235): abc.exe!boost::asio::ssl::detail::engine::perform
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\impl\engine.ipp (137): abc.exe!boost::asio::ssl::detail::engine::handshake
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\buffered_handshake_op.hpp (70): abc.exe!boost::asio::ssl::detail::buffered_handshake_op<boost::asio::const_buffer>::process<boost::asio::const_buffer const * __ptr64> + 0x1F bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\buffered_handshake_op.hpp (48): abc.exe!boost::asio::ssl::detail::buffered_handshake_op<boost::asio::const_buffer>::operator()
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\io.hpp (136): abc.exe!boost::asio::ssl::detail::io_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp>,boost::asio::ssl::detail::buffered_handshake_op<boost::asio::const_buffer>,boost::asio::executor_binder<std::_Binder<std::_Unforced,void (__cdecl CabcApplicationServerH + 0x50 bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\io.hpp (333): abc.exe!boost::asio::ssl::detail::async_io<boost::asio::basic_stream_socket<boost::asio::ip::tcp>,boost::asio::ssl::detail::buffered_handshake_op<boost::asio::const_buffer>,boost::asio::executor_binder<std::_Binder<std::_Unforced,void (__cdecl CabcApplicationServ + 0x87 bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\stream.hpp (505): abc.exe!boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >::async_handshake<boost::asio::const_buffer,boost::asio::executor_binder<std::_Binder<std::_Unforced,void (__cdecl CabcApplicationServerHttpSessionSSL::*)(boost::system::erro + 0x5E bytes
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\beast\experimental\core\ssl_stream.hpp (485): abc.exe!boost::beast::ssl_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >::async_handshake<boost::asio::const_buffer,boost::asio::executor_binder<std::_Binder<std::_Unforced,void (__cdecl CabcApplicationServerHttpSessionSSL::*)(boost::system::erro
c:\usr\work\abc_repo\util\capplicationserverhttpsession.cpp (343): abc.exe!CabcApplicationServerHttpSessionSSL::Run + 0x154 bytes
In some of the Leaks I also have:
c:\usr\work\abc_repo\ext\boost_1_68_0\boost\asio\ssl\detail\impl\engine.ipp (290): abc.exe!boost::asio::ssl::detail::engine::do_accept
Of course seems related to the ssl handshake but I check the session shutdown and seems ok.
Thank you in advance.
Every thread that uses async_handshake() leaks memory. I added OPENSSL_thread_stop() at the end of my thread procedure and it solved the issue.
Took it from here: https://github.com/openssl/openssl/issues/3033#issuecomment-289838302

HTTP Client Request Response with pion-net c++

I want to create a simple HTTPClient with pion-net (Pion-net leverages boost::asio.).
Boot Version 1.49
Pion-net Version 4.11
My client should just be a able to:
send a HTTP Request (this is working)
receive the HTTP Response (not working)
asynchronous code is not a must, synchronous would be ok
This is what I got:
#include <iostream>
#include "boost/asio.hpp"
#include "boost/thread.hpp"
#include "pion/net/HTTPRequestWriter.hpp"
#include "pion/net/HTTPResponseReader.hpp"
void FinishedResponseReading(pion::net::HTTPResponsePtr httpResponsePtr,
pion::net::TCPConnectionPtr tcpConnectionPtr,
const boost::system::error_code& errorCode_ref)
{
// ***************************
// this code is never reached!
// ***************************
std::cout << errorCode_ref << std::endl;
std::cout << httpResponsePtr->getContent() << std::endl;
tcpConnectionPtr->finish();
tcpConnectionPtr->close();
}
void FinishedRequestSending(const boost::system::error_code& error_code_ref,
pion::net::TCPConnectionPtr tcpConnectionPtr,
pion::net::HTTPRequest* httpRequest_ptr)
{
// ***************************
// this code is never reached!
// ***************************
pion::net::HTTPResponseReader::FinishedHandler fh =
boost::bind(FinishedResponseReading, _1, _2, _3);
pion::net::HTTPResponseReaderPtr httpResponseReaderPtr =
pion::net::HTTPResponseReader::create(tcpConnectionPtr,
*httpRequest_ptr,
fh);
httpResponseReaderPtr->receive();
}
int main()
{
boost::asio::io_service io_service;
// create and configure HTTPRequest
pion::net::HTTPRequest* httpRequest_ptr = new pion::net::HTTPRequest();
pion::net::HTTPRequestPtr httpRequestPtr(httpRequest_ptr);
httpRequest_ptr->setResource("/someService");
httpRequest_ptr->setMethod("PUT");
// create TCPConnection
pion::net::TCPConnection* tcpConnection_ptr =
new pion::net::TCPConnection(io_service);
pion::net::TCPConnectionPtr tcpConnectionPtr(tcpConnection_ptr);
// create HTTPRequestWriter
pion::net::HTTPRequestWriterPtr httpRequestWriterPtr(
pion::net::HTTPRequestWriter::create(tcpConnectionPtr,
httpRequestPtr,
boost::bind(FinishedRequestSending, _1,
tcpConnectionPtr,
httpRequest_ptr)));
// needed?
tcpConnection_ptr->setLifecycle(pion::net::TCPConnection::LIFECYCLE_KEEPALIVE);
// connect to server
tcpConnection_ptr->connect("192.168.1.14", 8080);
// send payload
httpRequestWriterPtr << "{\"someService\": \"something\"}";
httpRequestWriterPtr->send();
// ***********************************
// working fine so far! server is getting payload and is sending a HTTP Response
// but FinishedRequestSending is never reached
// ***********************************
// this is just to not exit immediately
boost::this_thread::sleep(boost::posix_time::milliseconds(15000));
// cleanup
delete(httpRequest_ptr);
delete(tcpConnection_ptr);
return 0;
}
If you want to communicate synchronously you could do something like this:
int main()
{
boost::asio::io_service io_service
pion::net::TCPConnection tcpConnection(io_service);
pion::net::HTTPRequest httpRequest;
httpRequest.setResource("/server/resource");
httpRequest.setMethod("PUT");
httpRequest.setMinor(1);
httpRequest.setMajor(1);
httpRequest.setContent("test");
boost::system::error_code ec;
ec = tcpConnection.connect("192.168.1.1", 80); // blocks till connected or timed out
if (!ec)
{
httpRequest.send(tcpConnection, ec); // never blocks
if (!ec)
{
pion::net::HTTPResponse(httpRequest);
httpResponse.receive(tcpConnection, ec); // this might block forever :-(
// httpResponse.receive seems to be IO dependent, you can set your socket to timeout
if (!ec)
{
httpResponse.write(std::cout, ec);
}
}
}
}
if however you need a little more sophisticated approach you could pion::net::HTTPResponseReader to wait asynch for a server response.
header:
class MyHTTPClient {
public:
void close();
pion::net::HTTPResponsePtr blockingReceiveOrTimeout(pion::net::HTTPRequest httpRequest, boost::system::error_code& ec_ref);
MyHTTPClient(boost::asio::ip::address aServerIP, unsigned int aServerPort);
virtual ~MyHTTPClient();
private:
boost::asio::ip::address mp_serverIP;
unsigned int mp_serverPort;
boost::asio::io_service mp_ioService;
pion::net::TCPConnectionPtr mp_tcpConnectionPtr;
pion::net::HTTPResponsePtr mp_curr_httpResponsePtr;
boost::system::error_code mp_curr_errorCode;
void finishedReceiveResponse(pion::net::HTTPResponsePtr httpResponsePtr, const boost::system::error_code& error_code_ref);
};
cpp:
MyHTTPClient::MyHTTPClient(boost::asio::ip::address aServerIP, unsigned int aServerPort) : mp_serverIP(aServerIP), mp_serverPort(aServerPort)
{
mp_tcpConnectionPtr.reset(new pion::net::TCPConnection(mp_ioService));
mp_tcpConnectionPtr->setLifecycle(pion::net::TCPConnection::LIFECYCLE_KEEPALIVE);
}
MyHTTPClient::~MyHTTPClient()
{
mp_tcpConnectionPtr->close();
}
void MyHTTPClient::close()
{
mp_tcpConnectionPtr->close();
}
pion::net::HTTPResponsePtr MyHTTPClient::blockingReceiveOrTimeout(pion::net::HTTPRequest httpRequest, boost::system::error_code& error_code_ref)
{
// reinit
mp_curr_httpResponsePtr.reset();
mp_ioService.reset();
error_code_ref.clear();
// connect to server if not already connectec
if (!mp_tcpConnectionPtr->is_open())
{
error_code_ref = mp_tcpConnectionPtr->connect(mp_serverIP, mp_serverPort);
}
if (!error_code_ref)
{
// send Request
httpRequest.send(*mp_tcpConnectionPtr.get(), error_code_ref, false);
if (!error_code_ref)
{
// asynchronously wait for response (times out automatically)
pion::net::HTTPResponseReader::FinishedHandler responseReaderFinishHandler = boost::bind(&MyHTTPClient::finishedReceiveResponse, this, _1, _3);
const pion::net::HTTPRequest constHTTPRequest = httpRequest;
pion::net::HTTPResponseReaderPtr httpResponseReaderPtr = pion::net::HTTPResponseReader::create(
mp_tcpConnectionPtr,
constHTTPRequest,
responseReaderFinishHandler);
httpResponseReaderPtr->receive();
mp_ioService.run();
}
}
return mp_curr_httpResponsePtr;
}
void MyHTTPClient::finishedReceiveResponse(pion::net::HTTPResponsePtr httpResponsePtr, const boost::system::error_code& error_code_ref)
{
mp_curr_httpResponsePtr = httpResponsePtr;
}
Martin's code updated for PION 5.0.3:
httpClient.hpp:
#ifndef httpClient_HPP_INCLUDED
#define httpClient_HPP_INCLUDED
#include <pion/error.hpp>
#include <pion/http/response.hpp>
#include <pion/tcp/connection.hpp>
#include <pion/http/response_reader.hpp>
#include <boost/asio.hpp>
class httpClient
{
public:
void close();
pion::http::response_ptr blockingReceiveOrTimeout(pion::http::request httpRequest, boost::system::error_code& ec_ref);
httpClient(boost::asio::ip::address aServerIP, unsigned int aServerPort);
virtual ~httpClient();
private:
boost::asio::ip::address mp_serverIP;
unsigned int mp_serverPort;
boost::asio::io_service mp_ioService;
pion::tcp::connection_ptr mp_tcpConnectionPtr;
pion::http::response_ptr mp_curr_httpResponsePtr;
boost::system::error_code mp_curr_errorCode;
void finishedReceiveResponse(pion::http::response_ptr httpResponsePtr, const boost::system::error_code& error_code_ref);
};
#endif // httpClient_HPP_INCLUDED
httpClient.cpp:
#include "httpClient.hpp"
httpClient::httpClient(boost::asio::ip::address aServerIP, unsigned int aServerPort) : mp_serverIP(aServerIP), mp_serverPort(aServerPort)
{
mp_tcpConnectionPtr.reset(new pion::tcp::connection(mp_ioService));
mp_tcpConnectionPtr->set_lifecycle(pion::tcp::connection::LIFECYCLE_KEEPALIVE);
}
httpClient::~httpClient()
{
mp_tcpConnectionPtr->close();
}
void httpClient::close()
{
mp_tcpConnectionPtr->close();
}
pion::http::response_ptr httpClient::blockingReceiveOrTimeout(pion::http::request httpRequest, boost::system::error_code& error_code_ref)
{
// reinit
mp_curr_httpResponsePtr.reset();
mp_ioService.reset();
error_code_ref.clear();
// connect to server if not already connectec
if (!mp_tcpConnectionPtr->is_open())
{
error_code_ref = mp_tcpConnectionPtr->connect(mp_serverIP, mp_serverPort);
}
if (!error_code_ref)
{
// send Request
httpRequest.send(*mp_tcpConnectionPtr.get(), error_code_ref, false);
if (!error_code_ref)
{
// asynchronously wait for response (times out automatically)
pion::http::response_reader::finished_handler_t responseReaderFinishHandler = boost::bind(&httpClient::finishedReceiveResponse, this, _1, _3);
const pion::http::request constHTTPRequest = httpRequest;
pion::http::response_reader_ptr httpResponseReaderPtr = pion::http::response_reader::create(
mp_tcpConnectionPtr,
constHTTPRequest,
responseReaderFinishHandler);
httpResponseReaderPtr->receive();
mp_ioService.run();
}
}
return mp_curr_httpResponsePtr;
}
void httpClient::finishedReceiveResponse(pion::http::response_ptr httpResponsePtr, const boost::system::error_code& error_code_ref)
{
mp_curr_httpResponsePtr = httpResponsePtr;
}
for completness main.cpp:
#include <iostream>
#include "httpClient.hpp"
int main (int argc, char *argv[])
{
pion::http::request request;
std::string requestData = "asdf";
request.set_content(requestData);
httpClient client(boost::asio::ip::address::from_string("10.1.1.100"), 80);
pion::http::response_ptr response;
boost::system::error_code ec;
response = client.blockingReceiveOrTimeout(request, ec);
response->write(std::cout, ec);
return 0;
}
I am not familiar with pion-net. However, based on the version I think is being used per the naming conventions, a quick scan through the pion-net code looks as though the io_service.run() needs to be invoked in the application code. The only place where I found pion-net explicitly invoking io_service.run() was in the PionScheduler types used by the Server types.

boost::asio::streambuf asserts "iterator out of bounds"

Client sends to server near about 165kB of data. At first all is fine.
But when client send the same data once again(165kB), I receive an assert on server side.
Assert contains information about "iterator out of bounds"
On the call stack, there is some information about read_until method.
So I think that I made a mistake.
TCP Asynchronous Server code is below:
Code for handle_read:
void Session::handle_read(const boost::system::error_code& a_error,
size_t a_nbytestransferred)
{
if (!a_error)
{
std::ostringstream dataToRetrive;
dataToRetrive << &m_bufferRead;
boost::thread threads(boost::bind(retriveMessageFromClient,
shared_from_this(), dataToRetrive.str()));
boost::asio::async_write(m_socket, m_bufferWrite,
boost::bind(&Session::handle_write,
shared_from_this(), boost::asio::placeholders::error));
}
else
disconnect();
}
Code for handle_write:
void Session::handle_write(const boost::system::error_code& a_error)
{
if (!a_error)
{
boost::asio::async_read_until(m_socket,
m_bufferRead, boost::regex(G_strREQUESTEND),
boost::bind(&Session::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
disconnect();
}
Both m_bufferRead, m_bufferWrite are members of class Session.
class Session...
boost::asio::streambuf m_bufferRead;
boost::asio::streambuf m_bufferWrite;
Update
I detected that problem is layed in other place of my code.
After than thread finishs tasks, metdhod
do_writeMessage() is called.
Thread function
void retriveMessageFromClient(boost::shared_ptr<Session>& A_spSesion, std::string A_strDataToRetrive)
{
try
{
std::string strAnswer;
bool bFind = (A_strDataToRetrive.find(G_REGEX_BIG_FILE_BEGIN) != std::string::npos);
if(bFind) // Write large data to osFile
{
A_strDataToRetrive = boost::regex_replace(A_strDataToRetrive, boost::regex(G_REGEX_BIG_FILE_BEGIN), std::string(""));
std::string strClientFolder = str(boost::format("%1%%2%") % CLIENT_PRE_FOLDER_FILE % A_spSesion->getIdentifier());
std::string strClientFile = str(boost::format("%1%\\%2%%3%") % strClientFolder % strClientFolder % CLIENT_EXTENSION);
if ( boost::filesystem::exists(strClientFolder) )
boost::filesystem::remove_all(strClientFolder);
else
boost::filesystem::create_directory( strClientFolder );
std::ofstream osFile(strClientFile.c_str());
osFile << A_strDataToRetrive;
osFile.close();
strAnswer = str(boost::format(G_FILE_WAS_WRITE) % strClientFile);
}
else
{
double dResult = sin (30.0 * 3.14/180);
strAnswer = str(boost::format(G_OPERATION_RESULT) % dResult);
}
// Sleep thread
boost::xtime timeToSleep;
boost::xtime_get(&timeToSleep, boost::TIME_UTC);
timeToSleep.sec += 2;
boost::this_thread::sleep(timeToSleep);
A_spSesion->do_writeMessage(strAnswer);
}
catch (std::exception& e)
{
std::cerr << THREAD_PROBLEM << e.what() << "\n";
}
}
Session do_writeMessage
void Session::do_writeMessage(const std::string& A_strMessage)
{
m_strMessage = A_strMessage;
m_strMessage += G_strRESPONSEEND;
// m_socket.send(boost::asio::buffer(m_strMessage)); It works correctly
m_socket.async_send(boost::asio::buffer(m_strMessage),
boost::bind(&Session::handle_write, shared_from_this(),
boost::asio::placeholders::error)); -- after that assert
}
So finnally I have a problem with asynch_send...
UPDATED
**TCPAsyncServer**::TCPAsyncServer(boost::asio::io_service& A_ioService, short port,
: m_ioService(A_ioService), m_lIDGenerator(0),
m_clientSocket(m_ioService, tcp::endpoint(tcp::v4(),
port)),
{
SessionPtr newSession(new Session(m_ioService, m_mapSessions, ++m_lIDGenerator));
m_clientSocket.async_accept(newSession->getSocket(),
boost::bind(&TCPAsyncServer::handle_ClientAccept, this,
newSession, boost::asio::placeholders::error));
Session contructor
Session::Session(boost::asio::io_service& A_ioService, std::map<long, boost::shared_ptr<Session> >& A_mapSessions, long A_lId)
: m_socket(A_ioService), m_mapSessions(A_mapSessions), m_lIdentifier(A_lId), m_ioService(A_ioService)
{}
Session members
std::map<long, boost::shared_ptr<Session> >& m_mapSessions;
long m_lIdentifier;
boost::asio::ip::tcp::socket m_socket;
boost::asio::io_service& m_ioService;
You need to use prepare, consume, and commit when using asio::streambuf to read and write from a socket. The documentation describes this with an example. It's not obvious to me based on your sample code if you are doing that.
writing
boost::asio::streambuf b;
std::ostream os(&b);
os << "Hello, World!\n";
// try sending some data in input sequence
size_t n = sock.send(b.data());
b.consume(n); // sent data is removed from input sequence
reading
boost::asio::streambuf b;
// reserve 512 bytes in output sequence
boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(512);
size_t n = sock.receive(bufs);
// received data is "committed" from output sequence to input sequence
b.commit(n);
std::istream is(&b);
std::string s;
is >> s;
If you are using async_read / async_read_until you don't need to specify a size for streambuf but do need to ensure the data you read into it is not greater than it maximum allowed size. In relation to the “iterator out of bounds” issue; I have found that telling asio to read when it's already reading causes a race condition for the streambuf to which asio reads which results in the assertion error:
Assert “iterator out of bounds”
You can use something like:
strand_.wrap(boost::bind(&your_class::handle_read, this,
asio::placeholders::error, asio::placeholders::bytes_transferred)));
to help synchronize your threads but you must be careful not to 'wrap' something that is already running with access to shared data.
HTH