Boost asio:async_read() using boost::asio::use_future - c++

When calling asio::async_read() using a future, is there a way to get the number of bytes transferred when a boost:asio::error::eof exception occurs? It would seem that there are many cases when one would want to get the data transferred even if the peer disconnects.
For example:
namespace ba = boost::asio;
int32_t Session::read (unsigned char* pBuffer, uint32_t bufferSizeToRead)
{
// Create a mutable buffer
ba::mutable_buffer buffer (pBuffer, bufferSizeToRead);
int32_t result = 0;
// We do an async call using a future. A thread from the io_context pool does the
// actual read while the the thread calling this method will blocks on the
// std::future::get()
std::future<std::size_t> future =
ba::async_read(m_socket, buffer, ba::bind_executor(m_sessionStrand, ba::use_future));
try
{
// We block the calling thread here until we get the results of the async_read_some()...
result = future.get();
}
catch (boost::system::system_error &ex) // boost::system::system_error
{
auto exitCode = ex.code().value();
if ( exitCode == ba::error::eof )
{
log ("Connection closed by the peer");
}
}
return results; // This is zero if eof occurs
}
The code sample above represents our issue. It was designed to support a 3rd-party library. The library expects a blocking call. The new code under development is using ASIO with a minimal number of network threads. The expectation is that this 3rd party library calls session::read using its dedicated thread and we adapt the call to an asynchronous call. The network call must be async since we are supporting many such calls from different libraries with minimal threads.
What was unexpected and discovered late is that ASIO treats a connection closed as an error. Without the future, using a handler we could get the bytes transferred up to the point where the disconnect occurred. However, using a future, the exception is thrown and the bytes transferred becomes unknown.
void handler (const boost::system::error_code& ec,
std::size_t bytesTransferred );
Is there a way to do the above with a future and also get the bytes transferred?
Or ss there an alternative approach where we can provide the library a blocking call by still use an asio::async_read or similar.
Our expectation is that we could get the bytes transferred even if the client closed the connection. We're puzzled that when using a future this does not seem possible.

It's an implementation limitation of futures.
Modern async_result<> specializations (that use the initiate member approach) can be used together with as_tuple, e.g.:
ba::awaitable<std::tuple<boost::system::error_code, size_t>> a =
ba::async_read(m_socket, buffer, ba::as_tuple(ba::use_awaitable));
Or, more typical:
auto [ec, n] = co_await async_read(m_socket, buffer, ba::as_tuple(ba::use_awaitable));
However, the corresponding:
auto future = ba::async_read(m_socket, buffer, ba::as_tuple(ba::use_future));
isn't currently supported. It arguably could, but you'd have to create your own completion token, or ask Asio devs to add support to use_future: https://github.com/chriskohlhoff/asio/issues
Side-note: if you construct the m_socket from the m_sessioStrand executor, you do not need to bind_executor to the strand:
using Executor = net::io_context::executor_type;
struct Session {
int32_t read(unsigned char* pBuffer, uint32_t bufferSizeToRead);
net::io_context m_ioc;
net::strand<Executor> m_sessionStrand{m_ioc.get_executor()};
tcp::socket m_socket{m_sessionStrand};
};

Related

Sending a large text via Boost ASIO

I am trying to send a very large string to one of my clients. I am mostly following code in HTTP server example: https://www.boost.org/doc/libs/1_78_0/doc/html/boost_asio/examples/cpp11_examples.html
Write callbacks return with error code 14, that probably means EFAULT, "bad address" according to this link:
https://mariadb.com/kb/en/operating-system-error-codes/
Note that I could not use message() member function of error_code to read error message, that was causing segmentation fault. (I am using Boost 1.53, and the error might be due to this: https://github.com/boostorg/system/issues/50)
When I try to send small strings, let's say of size 10 for example, write callback does not return with an error.
Here is how I am using async_write:
void Connection::do_write(const std::string& write_buffer)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(write_buffer, write_buffer.size()),
[this, self, write_buffer](boost::system::error_code ec, std::size_t transfer_size)
{
if (!ec)
{
} else {
// code enters here **when** I am sending a large text.
// transfer_size always prints 65535
}
});
}
Here is how I am using async_read_some:
void Connection::do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_),
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
if (!ec)
{
do_write(VERY_LARGE_STRING);
do_read();
} else if (ec != boost::asio::error::operation_aborted) {
connection_manager_.stop(shared_from_this());
}
});
}
What could be causing write callback to return with error with large string?
The segfault indicates likely Undefined Behaviour to me.
Of course there's to little code to tell, but one strong smell is from you using a reference to a non-member as the buffer:
boost::asio::buffer(write_buffer, write_buffer.size())
Besides that could simply be spelled boost::asio::buffer(writer_buffer), there's not much hope that write_buffer stays around for the duration of the asynchronous operation that depends on it.
As the documentation states:
Although the buffers object may be copied as necessary, ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
I would check that you're doing that correctly.
Another potential cause for UB is when you cause overlapping writes on the same socket/stream object:
This operation is implemented in terms of zero or more calls to the stream's async_write_some function, and is known as a composed operation. The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.
If you checked both these causes of concern and find that something must be wrong, please post a new question including a fully selfcontained example (SSCCE or MCVE)

How to wait for a function to return with Boost:::Asio?

Background
I'm new to using Boost::Asio library and am having trouble getting the behaviour I want. I am trying to implement some network communication for custom hardware solution. The communication protocol stack we are using relies heavily on Boost::Asio async methods and I don't believe it is entirely thread safe.
I have successfully implemented sending but encountered a problem when trying to setup the await for receiving. Most boost::asio examples I have found rely on socket behaviour to implement async await with socket_.async_read_some() or other similar functions. However this doesn't work for us as our hardware solution requires calling driver function directly rather than utilising sockets.
The application uses an io_service that is passed into boost::asio::generic::raw_protocol::socket as well as other classes.
Example code from protocol stack using sockets
This is the example code from the protocol stack. do_receive() is called in the constructor of RawSocketLink.
void RawSocketLink::do_receive()
{
namespace sph = std::placeholders;
socket_.async_receive_from(
boost::asio::buffer(receive_buffer_), receive_endpoint_,
std::bind(&RawSocketLink::on_read, this, sph::_1, sph::_2));
}
void RawSocketLink::on_read(const boost::system::error_code& ec, std::size_t read_bytes)
{
if (!ec) {
// Do something with received data...
do_receive();
}
}
Our previous receive code without the protocol stack
Prior to implementing the stack we had been using the threading library to create separate threads for send and recieve. The receive method is shown below. Mostly it relies on calling the receive_data() function from the hardware drivers and waiting for it to return. This is a blocking call but is required to return data.
void NetworkAdapter::Receive() {
uint8_t temp_rx_buffer[2048];
rc_t rc;
socket_t *socket_ptr;
receive_params_t rx_params;
size_t rx_buffer_size;
char str[100];
socket_ptr = network_if[0];
while (1) {
rx_buffer_size = sizeof(temp_rx_buffer);
// Wait until receive_data returns then process
rc = receive_data(socket_ptr,
temp_rx_buffer,
&rx_buffer_size,
&rx_params,
WAIT_FOREVER);
if (rc_error(rc)) {
(void)fprintf(stderr, "Receive failed");
continue;
}
// Do something with received packet ....
}
return;
}
Note that the socket_t pointer in this code is not the same thing as a TCP/UDP socket for Boost::Asio.
Current implement of async receive
This is my current code and where I need help. I'm not sure how to use boost::asio method to wait for receive_data to return. We are trying to replicate the behaviour of socket.async_read_from(). The NetworkAdapter has access to the io_service.
void NetworkAdapter::do_receive() {
rc_t rc;
socket_t *socket_ptr;
receive_params_t rx_params;
size_t rx_buffer_size;
socket_ptr = network_if[0];
rx_buffer_size = receive_buffer_.size();
// What do I put here to await for this to return asynchronously?
rc = receive_data(socket_ptr, receive_buffer_.data(), &rx_buffer_size, &rx_params, ATLK_WAIT_FOREVER);
on_read(rc, rx_buffer_size, rx_params);
}
void NetworkAdapter::on_read(const rc_t &rc, std::size_t read_bytes, const receive_params_t &rx_params) {
if (!rc) {
// Do something with received data...
} else {
LOG(ERROR) << "Packet receieve failure";
}
do_receive();
}
Summary
How do I use boost::asio async/await functions to await a function return? In particular I want to replicate the behaviour of socket.async_receive_from() but with a function rather than a socket.
*Some function names and types have been changed due to data protection requirements.
N4045 Library Foundations for Asynchronous Operations, Revision 2
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4045.pdf
On page 24 there is an example on how to implement an asio async API in terms of callback-based os API.
// the async version of your operation, implementing all kinds of async paradigm in terms of callback async paradigm
template <class CompletionToken>
auto async_my_operation(/* any parameters needed by the sync version of your operation */, CompletionToken&& token)
{
// if CompletionToken is a callback function object, async_my_operation returns void, the callback's signature should be void(/* return type of the sync version of your operation, */error_code)
// if CompletionToken is boost::asio::use_future, async_my_operation returns future</* return type of the sync version of your operation */>
// if CompletionToken is ..., ...
// you are not inventing new async paradigms so you don't have to specialize async_completion or handler_type, you should focus on implement the os_api below
  async_completion<CompletionToken, void(/* return type of the sync version of your operation, */error_code)/* signature of callback in the callback case */> completion(token);
  typedef handler_type_t<CompletionToken, void(error_code)> Handler;
  unique_ptr<wait_op<Handler>> op(new wait_op<Handler>(move(completion.handler))); // async_my_operation initates your async operation and exits, so you have to store completion.handler on the heap, the completion.handler will be invoked later on a thread pool (e.g. threads blocked in IOCP if you are using os api, threads in io_context::run() if you are using asio (sockets accept an io_context during construction, so they know to use which io_context to run completion.handler))
  
// most os api accepts a void* and a void(*)(result_t, void*) as its C callback function, this is type erasure: the void* points to (some struct that at least contains) the C++ callback function object (can be any type you want), the void(*)(result_t, void*) points to a C callback function to cast the void* to a pointer to C++ callback function object and call it
  os_api(/* arguments, at least including:*/ op.get(), &wait_callback<Handler>);
  return completion.result.get();
}
// store the handler on the heap
template <class Handler>
struct wait_op {
  Handler handler_;
  explicit wait_op(Handler  handler) : handler_(move(handler)) {}
};
// os post a message into your process's message queue, you have several threads blocking in a os api (such as IOCP) or asio api (such as io_context::run()) that continuously takes message out from the queue and then call the C callback function, the C callback function calls your C++ callback function
template <class Handler>
void wait_callback(result_t result, void* param)
{
  unique_ptr<wait_op<Handler>> op(static_cast<wait_op<Handler>*>(param));
  op‐>handler_(/* turn raw result into C++ classes before passing it to C++ code */, error_code{});
}
//trivial implementation, you should consult the socket object to get the io_context it uses
void os_api(/* arguments needed by your operation */, void* p_callback_data, void(*p_callback_function)(result_t, void*))
{
std::thread([](){
get the result, blocks
the_io_context_of_the_socket_object.post([](){ (*p_callback_function)(result, p_callback_data); });
}).detach();
}
boost.asio has changed from async_completion and handler_type to async_result, so the above code is outdated.
Requirements on asynchronous operations - 1.75.0
https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/asynchronous_operations.html

Reading from one socket for several consumers asynchronously in one thread

I am implementing a connection multiplexer - class, which wraps a single connection in order to provide an ability to create so-called Stream-s over it. There can be dozens of such streams over one physical connection.
Messages sent over that connection are defined by a protocol and can be service ones (congestion control, etc), which are never seen by the clients, and data ones - they contain some data for the streams, for which one - defined in the header of the corresponding message.
I have encountered a problem when implementing a method read for a Stream. It must be blocking, but asynchronous, so that it returns some value - data read or error happened - but the request itself must be is some kind of async queue.
To implement asynchronous network IO we have used Boost's async_read-s, async_write-s, etc with a completion token, taken from another library. So, a call to MyConnection::underlying_connection::read(size_t) is asynchronous already in the terms I described before.
One solution I have implemented is function MyConnection::processFrame(), which is reading from the connection, processing message and, if it is a data message, puts the data into the corresponding stream's buffer. The function is to be called in a while loop by the stream's read. But, in that case there can be more than one simulteneous calls to async_read, which is UB. Also, this would mean that even service messages are to wait until some stream wants to read the data, which is not appropriate as well.
Another solution I came up is using future-s, but as I checked, their methods wait/get would block the whole thread (even with defered policy or paired promise), which must be avoided too.
Below is a simplified example with only methods, which are needed to understand the question. This is current implementation, which contains bugs.
struct LowLevelConnection {
/// completion token of 3-rd part library - ufibers
yield_t yield;
/// boost::asio socket
TcpSocket socket_;
/// completely async (in one thread) method
std::vector<uint8_t> read(size_t bytes) {
std::vector<uint8_t> res;
res.reserve(bytes);
boost::asio::async_read(socket_, res, yield);
return res;
}
}
struct MyConnection {
/// header is always of that length
constexpr uint32_t kHeaderSize = 12;
/// underlying connection
LowLevelConnection connection_;
/// is running all the time the connection is up
void readLoop() {
while (connection_.isActive()) {
auto msg = connection_.read(kHeaderSize);
if (msg.type == SERVICE) { handleService(msg); return; }
// this is data message; read another part of it
auto data = connection_.read(msg.data_size);
// put the data into the stream's buffer
streams_.find(data.stream_id).buffer.put(data);
}
}
}
struct Stream {
Buffer buffer;
// also async blocking method
std::vector<uint8_t> read(uint32_t bytes) {
// in perfect scenario, this should look like this
async_wait([]() { return buffer.size() >= bytes; });
// return the subbuffer of 'bytes' size and remove them
return subbufer...
}
}
Thanks for future answers!

Boost.Asio: Could I cancel a SYNCHRONOUS operation running in a secondary thread?

I am using Boost.Asio and Boost.Thread. How can I cancel a synchronous IO operation in a secondary Thread?
For example,
/* consider needed headers included and typedefs are defined */
void OperationThread(sock_ptr sock) {
char buf[128];
boost::system::error_code ec;
boost::asio::read(*sock, boost::asio::buffer(buf, 128), ec);
}
void AcceptorThread() {
boost::asio::io_service serv;
boost_asio_endpoint_type ep( ... /* v4() etc. */ , ... /* port */ );
boost_asio_acceptor_type acc(serv, ep);
sock_ptr sock(new boost_asio_socket_type(serv));
while (true) {
acc.accept(*sock);
boost::thread t(boost::bind(&OperationThread, sock)); // new thread
sock.reset(new boost_asio_socket_type(serv)); // new socket
}
}
int main() {
boost::thread accthread(&AcceptorThread);
cin.get();
// Code to cancel synchronous accept operation and read operations
return 0;
}
sock_ptr is a typedef of boost::shared_ptr<boost_asio_socket_type>.
If you get confused about boost_asio_*_type consider they are configured for boost::asip::ip::tcp and v4().
First of all I used to use asynchronous operations. But you know they are a little bit hard to manage. And I want one-thread-per-connection application model. It's harder to implement it using asynchronous IO (actually I don't know how to implement it, how can I poll a specific asynchronous operation in a thread?, I'd be happy if you mention.). So I use synchronous IO.
And in my code, as you would notice, my thread object destroyed at the end of while block. Does it make any harm? (sorry I know it is not appropriate to ask it in this question, but anyone who wants to do what I am trying to do may face this question. So if you answer, I will be very happy. ʘ‿ʘ)
Thanks.
Yes you can, as long as you propoerly synchronize access to the service object (e.g. boost::asio::ip::tcp::socket or boost::asio::deadline_timer)¹.
In your code, additionally, you'd be using the shared_ptr<> from different threads. Shared pointers are not thread safe per se. Although you can use the atomic_load and atomic_store functions with them to make it thread-safe.
¹ The documentation reveals the (exceptional) cases where objects are thread safe. The only two I can cite from memory are boost::asio::io_service and boost::asio::strand

Should I Use Blocking or Asynchronous boost::asio::socket Read/Write to Implement a Protocol Handshake

I'm implementing an RTMP protocol using boost::asio::socket.
After the async_accept, the protocol requires a 3-step handshake. See the code below:
.
.
.
void RtmpServer::StartAsyncAccept()
{
// Create a new connection
nextConn = RtmpConnection::Create(this, ios);
// FIXME: shall we use async or blocking accept???
acceptor.async_accept
(
nextConn->GetSocket(),
boost::bind
(
&RtmpServer::HandleAsyncAccept,
this,
boost::asio::placeholders::error
)
);
}
.
.
.
void RtmpServer::HandleAsyncAccept(const boost::system::error_code& ec)
{
if (!ec)
{
if (nextConn->StartHandshake())
{
// Push the current connection to the queue
AddConnection(nextConn);
boost::array<char, 0> dummyBuffer;
nextConn->GetSocket().async_read_some
(
// TODO: use a strand for thread-safety.
boost::asio::buffer(dummyBuffer), // FIXME: Why boost::asio::null_buffers() not working?
boost::bind
(
&RtmpConnection::HandleData,
nextConn,
boost::asio::placeholders::error
)
);
}
}
// Start to accept the next connection
StartAsyncAccept();
}
The RtmpConnection::StartHandshake will return true if the handshake succeeded (then RtmpConnection::HandleData will be called), false otherwise (connection aborted, not handled yet).
There are 3 main steps for the handshake, each involves Cx and Sx messages, i.e., C{0,1,2}, S{0,1,2}.
The basic handshake MUST follow:
// HANDSHAKE PROTOCOL
// Handshake Sequence:
// The handshake begins with the client sending the C0 and C1 chunks.
//
// The client MUST wait until S1 has been received before sending C2.
// The client MUST wait until S2 has been received before sending any
// other data.
//
// The server MUST wait until C0 has been received before sending S0 and
// S1, and MAY wait until after C1 as well. The server MUST wait until
// C1 has been received before sending S2. The server MUST wait until C2
// has been received before sending any other data.
As you may have noticed that, (as usual), a handshake requires waiting. For example,
The server MUST wait util C0 has been received before sending S0. In our case C0 only contains a one-byte version integer, and the server has to verify if the version is valid or not, then send S0 to the client.
And so on, similar as C1/S1, C2/S2 (but slightly different).
My question is, should I use blocking Read/Write for this handshake, or asynchronous?
Currently I'm using blocking Read/Write, which is easier to implement.
However, I googled a lot, finding out that a lot of guys suggest asynchronous read/write, because they have better performance and more flexibility.
I'm asking if I want to implement it using asynchronous socket read/write, what should I do? Should I create a bunch of handlers for these 3 main steps? or any other better suggestions.
Sample pseudo code will be appreciated.
The typical two approaches are:
async. operations with a small number of threads
sync. operations with one thread per connection/client
I believe it's well established that in terms of scalability, (1) beats (2), but in terms of simplicity of the code (2) typically beats (1). If you don't expect to handle more than a few connections ever, you might want to consider (2).
It's possible to use coroutines to make your asynchronous code look synchronous, and get the best of both worlds. However, there's no platform independent way of doing it, and it might get quite messy, since there's also no standard way to do it. I believe it's not done very often.
One simple way to use async. operations is to have an implicit state machine, based in which callback will be used when there is more data to read from (or write to) the socket. That would look something like this (simplified):
class my_connection {
tcp::socket m_sock;
char m_buf[1024];
int m_pos;
void async_handshake(size_t bytes_transferred, error_code& ec) {
if (ec) { ... }
m_pos += bytes_transferred;
if (m_pos == handshake_size) {
parse_handshake();
return;
}
m_sock.async_read(asio::buffer(m_buf + m_pos, handshake_size - m_pos), boost::bind(&async_handshake, shared_from_this(), _1, _2));
}
void parse_handshake()
{
// ...
m_pos = 0;
// ... fill m_buf with handshake ...
async_write_handshake();
}
void async_write_handshake(size_t bytes_transferred, error_code& ec) {
if (ec) { ... }
m_pos += bytes_transferred;
if (m_pos == handshake_size) {
handshake_written();
return;
}
m_sock.async_write_some(asio::buffer(m_buf + m_pos, handshake_size - m_pos), boost::bind(&async_write_handshake, shared_from_this(), _1, _2));
}
void handshake_written() { /* ... */ }
};
This may not be very sustainable once the protocol gets more complicated though. To deal with that, it might be simpler to have an explicit state machine in your connection class, and you have a single read callback, and a single write callback. Whenever some data has been written or read, you perform an action based on which state the connection is in.