Replacing boost::asio::async_read_some with boost::asio::async_read - c++

I was using the code sample provided by boost, the echo server, to try something with one of my client.
However, when I tried to replace the boost::asio::async_read_some with boost::asio::async_read, I noticed that the latter would only be called when the connection would be killed and read_some would be called any time a message would be sent from one of my client. What am I doing wrong?
Here is the original code
https://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
I replaced the content of do_read() with the following:
void do_read()
{
auto self = shared_from_this();
boost::asio::async_read(socket_, data_, [this, self](boost::system::error_code ec, size_t l) {
if (!ec)
do_write(l);
});
}

Related

How does boost::beast::bind_front_handler works?

I am trying boost::beast examples, I came across to this piece of code.
void on_write(beast::error_code ec, std::size_t byte_transferred) {
if (ec) return fail(ec, "write");
http::async_read(m_tcp_stream, m_buffer, m_response, beast::bind_front_handler(
&Session::on_read, shared_from_this()));
}
void on_read(beast::error_code ec, std::size_t bytes_transferred) {
if (ec) return fail(ec, "read");
//std::cout << m_response << std::endl;
write_on_file(m_response);
m_tcp_stream.socket().shutdown(tcp::socket::shutdown_both, ec);
if (ec && ec != beast::errc::not_connected) return fail(ec, "showdown");
}
Particularly http::async_read(m_tcp_stream, m_buffer, m_response, beast::bind_front_handler(&Session::on_read, shared_from_this())); this line. I am not able to understand its code. How does it work. As far as I get from the code, that It returns bind_front_wrapper which constructs a Handler and tuple of args within itself. But I did not understand how does it manage to get the arguments of the passed Handler in bind_front_handler even though we are not passing, we are just passing shared_ptr. In this case async_read is calling on_read method. But we are not passing any parameters, but still it get called, I wonder how?
You use asynchronous operations, so your job is to define callbacks which are called
by Beast core code when operations are completed. When an operation started by async_read is ready,
handler passed to async_read is called with two arguments: error code + number of transferred bytes.
You decided to wrap on_read into callback by bind_front_handler. bind_front_handler generates a functor object
whose implementation in pseudocode may look like:
class Handler {
void (Session::*onRead)(...); // pointer to on_read function member of Session
Session* session; // pointer to session, get by shared_from_this
Handler(/* pointer to on_read, pointer to session */) {}
template<class ... Args>
void operator() (Args... args) {
((*session).*onRead)(args...);
}
}
when read operation is ready, function operator() of above handler is called with two arguments pack: error code
and number of read bytes.
Since c++20 there is std::bind_front,
you may visit reference to get more details how it could be implemented in Beast library.

multithreading problem in boost asio example

I'm developing a tcp service, and I took an example from boost asio to start (https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/example/cpp11/chat/chat_server.cpp), and I'm worried about something, as I understand, any time you want to send something you have to use the deliver function that check the status of and run some operations over the write_msgs_ queue (in my code write_msgs_ is a queue of std::byte based structures):
void deliver(const chat_message& msg)
{
bool write_in_progress = !write_msgs_.empty();
write_msgs_.push_back(msg);
if (!write_in_progress)
{
do_write();
}
}
and inside the do_write() function you will see an asynchronous call wrapping a lambda function:
void do_write()
{
auto self(shared_from_this());
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
write_msgs_.pop_front();
if (!write_msgs_.empty())
{
do_write();
}
}
else
{
room_.leave(shared_from_this());
}
});
}
where the call is constantly sending messages until the queue is empty.
Now, as I understand, the boost::asio::async_write make the lambda function thread safe, but, as the write_msgs_ is open to be used in the deliver function which is out of the isolation given by the io_context a mutex is needed. Now, should I put a mutex each time the write_queue is used or is cheaper to use the boost::asio::post() calling the deliver function to isolate the write_msgs_ from asynchronous calls ?
something like this:
boost::asio::io_service srvc; // this somewhere
void deliver2(const chat_message &msg)
{
srvc.post(std::bind(&chat_session::deliver,this,msg));
}

C++ wait for all async operation end

I have started N same async operations(e.g. N requests to database), so i need to do something after all this operations end. How i can do this? (After one async operation end, my callback will be called).
I use C++14
Example
i use boost.asio to write some data to socket.
for (int i = 0; i < N; ++i)
{
boost::asio::async_write(
m_socket,
boost::asio::buffer(ptr[i], len[i]),
[this, callback](const boost::system::error_code& ec, std::size_t )
{
callback(ec);
});
}
So i need to know when all my writes ends;
first of all, never call async_write in a loop. Each socket may have only one async_write and one async_read outstanding at any one time.
boost already has provision for scatter/gather io.
This snippet should give you enough information to go on.
Notice that async_write can take a vector of vectors as a 'buffer' and it will fire the handler exactly once, once all the buffers have been written.
struct myclass {
boost::asio::ip::tcp::socket m_socket;
std::vector<std::vector<char>> pending_buffers;
std::vector<std::vector<char>> writing_buffers;
void write_all()
{
assert(writing_buffers.size() == 0);
writing_buffers = std::move(pending_buffers);
boost::asio::async_write(
m_socket,
boost::asio::buffer(writing_buffers),
std::bind(&myclass::write_all_handler,
this,
std::placeholders::_1,
std::placeholders::_2));
}
void write_all_handler(const boost::system::error_code& ec, size_t bytes_written)
{
writing_buffers.clear();
// send next load of data
if (pending_buffers.size())
write_all();
// call your callback here
}
};

what is the easiest way to extend an existing data structure to include semaphore or similar methods in c++?

I have an existing c++ code.
boost::asio::ip::address m_sender_IP_address;
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
boost::bind(&Udp_comm::handle_receive, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Udp_comm::handle_receive(const boost::system::error_code& error, const std::size_t bytes_transferred)
{
std::string recvd_message(m_recv_buffer.begin(), m_recv_buffer.begin() + bytes_transferred);
m_sender_IP_address = m_sender_endpoint.address();//////////wait here
//continue to listening to future messages
start_receive();
process_message(m_sender_IP_addres.to_string(), recvd_message);
}
How to protect m_sender_IP_address from getting overwritten by next UDP message received? I want lock access to that variable from 2nd line in handle_receive method to the 1st line in process_message method
can I also acheive something similar without using locks?
A semaphore is not necessary here. You can use a strand to ensure an asynchronous handler is invoked concurrently once at most. To do this, create a wrapped handler for the async_receieve_from() callback
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
m_strand.wrap(
boost::bind(
&Udp_comm::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
)
);
}
Here m_strand is a member of Udp_comm. Doing this will guarantee only one thread can mutate m_sender_IP_address.

Boost Asio async_read doesn't stop reading?

So,
I've been playing around with the Boost asio functions and sockets (specifically the async read/write). Now, I thought that boost::asio::async_read only called the handler when a new buffer came in from the network connection... however it doesn't stop reading the same buffer and thus keeps calling the handler. I've been able to mitigate it by checking the number of bytes transferred, however it is basically in a busy-waiting loop wasting CPU cycles.
Here is what I have:
class tcp_connection : : public boost::enable_shared_from_this<tcp_connection>
{
public:
// other functions here
void start()
{
boost::asio::async_read(socket_, boost::asio::buffer(buf, TERRAINPACKETSIZE),
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
const unsigned int TERRAINPACKETSIZE = 128;
char buf[TERRAINPACKETSIZE];
void handle_read(const boost::system::error_code& error, size_t bytesT)
{
if (bytesT > 0)
{
// Do the packet handling stuff here
}
boost::asio::async_read(socket_, boost::asio::buffer(buf, TERRAINPACKETSIZE),
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
};
Some stuff is cut out, but basically a new connection gets created then start() is called. Is there something I'm missing so that the handle_read method doesn't get continuously called?
A wild guess: Do you check error in handle_read? If the socket is in an error state for some reason, I guess that the nested call to async_read made from handle_read will immediately "complete", resulting in an immediate call to handle_read
I was having the same problem. In my case, I was reading into a std::vector<unsigned_char> like this:
boost::asio::async_read(socket_,
st::asio::buffer(*message,message->size()),
boost::bind(
&ActiveSocketServerSession::handleFixLengthRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
I have an adaptative vector for accept a variant number of bytes
std::vector<unsigned char>* message;
message = new std::vector<unsigned char> (sizePacket);
When I was receiving the first packet all was going fine, but after the first, never stops unlocking handle_reader with no data.
My solution was to delete my vector and alloc space again after processing it:
void ActiveSocketServerSession::handleFixLengthRead( const boost::system::error_code& error,
std::size_t bytes_transferred){
--> processing your data (save to another site)
--> delete message;
--> message = new std::vector<unsigned char> (sizePacket);
//starting to read again
boost::asio::async_read(socket_,
boost::asio::buffer(*message,message->size()),
boost::bind(
&ActiveSocketServerSession::handleFixLengthRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
}else{
logMessage.str("");
logMessage << "Error handling data id: "<<getId()<< "from port";
}
}
After putting these two lines all goes fine.