I'm trying to learn modern C++ and I'm using Boost.Asio for networking. I wrote a TCP connection class, which uses Asio's asynchronous operations. This is currently my method for reading data from a socket:
template<class T>
inline auto connection<T>::read(size_t length) -> void
{
auto handler = [&](const boost::system::error_code& error, size_t bytes_transferred) {
if (error == boost::asio::error::eof or error == boost::asio::error::connection_reset) {
close();
} else {
on_read(bytes_transferred);
}
};
socket.async_read_some(boost::asio::buffer(read_buffer, length), handler);
}
Here I declared the read handler separately with auto, because I think it looks more readable than an in-place lambda, i.e.
template<class T>
inline auto connection<T>::read(size_t length) -> void
{
socket.async_read_some(boost::asio::buffer(read_buffer, length), [&](const boost::system::error_code& error, size_t bytes_transferred) {
if (error == boost::asio::error::eof or error == boost::asio::error::connection_reset) {
close();
} else {
on_read(bytes_transferred);
}
});
}
However I ran into a segmentation fault with the first version, and I believe this is because the handler lambda is lost when the method goes out of scope. Then I tried to move the handler with std::move
socket.async_read_some(boost::asio::buffer(read_buffer, length), std::move(handler));
which seems to fix the segfault.
Now my question is: Are there any performance or other issues with using the first version (with std::move) vs in-place? Which one do you think is better practice?
Both of these code examples should work. The first example passes the handler as an lvalue, in which case the implementation will make a copy. The second example passes a lambda as a prvalue, in which case the implementation will perform a move-construction. As both the lvalue and prvalue are trivial, the two operations are the same.
Asynchronous initiating functions in Networking TS (and by extension, Asio and Boost.Asio) take ownership of handlers by performing a "decay-copy." That means the handler is either copied or moved from depending on whether the argument is an lvalue or not.
I am not sure why your first example crashes, but it has nothing to do with the lifetime of the lambda. For obvious reasons, asynchronous initiating functions never receive the handle by reference, and always take ownership by decay-copy.
There must be some other problem with your code, in the part that you haven't pasted. For example, what is keeping the connection object alive after the function returns?
Related
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)
I have a program (client + server) that works with no issue with this write:
boost::asio::write(this->socket_, boost::asio::buffer(message.substr(count,length_to_send)));
where socket_ is boost::asio::ssl::stream<boost::asio::ip::tcp::socket> and message is an std::string.
I would like to make this better and non-blocking, so I created a function that could replace this, it's called like follows:
write_async_sync(socket_,message.substr(count,length_to_send));
The purpose of this function is:
To make the call async, intrinsically
To keep the interface unchanged
The function I implemented simply uses promise/future to simulate sync behavior, which I will modify later (after it works) to be cancellable:
std::size_t
SSLClient::write_async_sync(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket,
const std::string& message_to_send)
{
boost::system::error_code write_error;
std::promise<std::size_t> write_promise;
auto write_future = write_promise.get_future();
boost::asio::async_write(socket,
boost::asio::buffer(message_to_send),
[this,&write_promise,&write_error,&message_to_send]
(const boost::system::error_code& error,
std::size_t size_written)
{
logger.write("HANDLING WRITING");
if(!error)
{
write_error = error;
write_promise.set_value(size_written);
}
else
{
write_promise.set_exception(std::make_exception_ptr(std::runtime_error(error.message())));
}
});
std::size_t size_written = write_future.get();
return size_written;
}
The problem: I'm unable to get the async functionality to work. The sync one works fine, but async simply freezes and never enters the lambda part (the writing never happens). What am I doing wrong?
Edit: I realized that using poll_one() makes the function execute and it proceeds, but I don't understand it. This is how I'm calling run() for io_service (before starting the client):
io_service_work = std::make_shared<boost::asio::io_service::work>(io_service);
io_service_thread.reset(new std::thread([this](){io_service.run();}));
where basically these are shared_ptr. Is this wrong? Does this way necessitate using poll_one()?
Re. EDIT:
You have the io_service::run() correctly. This tells me you are blocking on the future inside a (completion) handler. That, obviously, prevents run() from progressing the event loop.
The question asked by #florgeng was NOT whether you have an io_service instance.
The question is whether you are calling run() (or poll()) on it suitably for async operations to proceed.
Besides, you can already use future<> builtin:
http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/overview/cpp2011/futures.html
Example: http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/example/cpp11/futures/daytime_client.cpp
std::future<std::size_t> recv_length = socket.async_receive_from(
boost::asio::buffer(recv_buf),
sender_endpoint,
boost::asio::use_future);
Are nested boost::bind permissible, and if so what am I doing wrong? I can nest lambda in bind successfully, but not bind in bind.
First example
The simple case
I can manage the standard use boost::bind to pass a complex completion handler invocation where a simple one taking only error code is needed:
socket->receive(buffer, boost::bind(...));
Nested case
but if I want to encapsulate a combination of boost asio operations (e.g. multi-stage async_connect and async_ssl_handshake).
My outer operation will be something like:
connect_and_ssl(socket, boost::bind(...));
and my first stage definition will pass the outer handler on to the second completion in another bind, so that the outer handler can be invoked at the end:
template<typename Socket, typename Handler>
void connect_and_ssl(Socket socket, Handler handler)
{
socket.async_connect(endpoint,
boost::bind(&w::handle_connect, this, socket, handler, boost::asio::placeholders::error));
};
template<typename Socket, typename Handler>
void handle_connect(Socket socket, Handler handler, const boost::system::error_code& ec) {
socket->async_handshake(handler);
}
however handler which is a boost::bind really does not like being part of another boost bind. I get a whole screen full of errors, about not being able to determine the type, and others.
Lambdas work
But I find that I can easily use lambdas instead:
template<typename Socket, typename Handler>
void connect_and_ssl(Socket socket, Handler handler)
{
socket.async_connect(endpoint,
[=](const boost::system::error_code& ec) { handle_connect(socket, handler, ec); } );
};
why? Lambdas are so much easier to write, and understand, but do they make possible something that was impossible with nested binds, or was I just expressing the binds wrongly?
Second example
Simple case
although this will compile:
m_ssl_socket->async_read_some(buffer, m_strand->wrap(handler));
Nested case
when converting to be also invoked from a strand:
m_strand->post(boost::bind(&boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>::async_read_some, m_ssl_socket, buffer, m_strand->wap(handler)));
it will no longer compile - no doubt due to the strand->wrap being inside a boost::bind
Lambda
However the lamda version compiles and runs fine:
m_strand->post([=](){m_ssl_socket->async_read_some(buffer, m_strand->wrap(handler)); } );
I can't work it out, but I'm very glad for lamdas.
Nested bind requires protect.
Boost Bind has it.
In C++11 you have to define one yourself (e.g. using reference_wrapper).
I want to use std::functions for callback parameters in a wrapper class.
The class wraps a library that allows asynchronous TCP/IP operations (actually boost::asio but neither boost::asio nor TCP/IP should matter here, only that it has asynchronous operations).
The library functions allow me to pass another callback function object that is asynchronously called when the requested operation is finished.
Depending on the result of the asynchronous operation I want to invoke the callback specified by my client or start further operations.
The following code tries to sketch what I intend.
using ConnectHandler = std::function<void(boost::system::error_code ec)>;
class MyConnection
{
public:
void Connect(ConnectHandler handler); // (1)
}
void MyConnection::Connect(ConnectHandler handler)
{
SomeLibrary::async_connect(...,
[handler](boost::system::error_code ec, ...) // (2)
{
//Depending on ec start a nested read/write operation.
//One way or another it finally invokes the callback
handler(ec); // (3)
});
}
The client code would look something like this
MyConnection conn;
conn.Connect([](boost::system::error_code ec)
{
//check ec and start read/write operation
});
My question is:
what is the best way to declare my Connect method in (1), f.e
void Connect(ConnectHandler handler);
void Connect(const ConnectHandler& handler);
void Connect(ConnectHandler&& handler);
and depending on that how do I correctly capture the callback handler in the lambda capture clause in (2) such that I can call it in (3)?
A side note:
the clients instance of MyConnection will never go out of scope until all asynchronous operations have completed!
std::function are cheap to move, so taking it by value is acceptable. Taking by && is mostly pointless, as at best is saves a move. And it forces the caller to move, not copy, and maybe the caller wants to copy?
They are not cheap to copy, so you could consider capturing by move in your callable object.
In C++14, this is as simple as:
[handler=std::move(handler)]
as a capture list (generalized capture expressions).
In C++11 you need to write a custom object to do this.
struct custom_work {
ConnectHandler handler;
void operator()(boost::system::error_code ec, ...) const {
//Depending on ec start a nested read/write operation.
//One way or another it finally invokes the callback
handler(ec); // (3)
}
};
then
SomeLibrary::async_connect(...,
some_work{std::move(handler)}
);
which has the disadvantage of moving the code from inline to out of line.
I'm pretty new to boost. I needed a cross platform low level C++ network API, so I chose asio. Now, I've successfully connected and written to a socket, but since I'm using the asynchronous read/write, I need a way to keep track of the requests (to have some kind of IDs, if you will). I've looked at the documentation/reference, and I found no way to pass user data to my handler, the only option I can think of is creating a special class that acts as a callback and keeps track of it's id, then pass it to the socket as a callback. Is there a better way? Or is the best way to do it?
The async_xxx functions are templated on the type of the completion handler. The handler does not have to be a plain "callback", and it can be anything that exposes the right operator() signature.
You should thus be able to do something like this:
// Warning: Not tested
struct MyReadHandler
{
MyReadHandler(Whatever ContextInformation) : m_Context(ContextInformation){}
void
operator()(const boost::system::error_code& error, std::size_t bytes_transferred)
{
// Use m_Context
// ...
}
Whatever m_Context;
};
boost::asio::async_read(socket, buffer, MyReadHander(the_context));
Alternatively, you could also have your handler as a plain function and bind it at the call site, as described in the asio tutorial. The example above would then be:
void
HandleRead(
const boost::system::error_code& error,
std::size_t bytes_transferred
Whatever context
)
{
//...
}
boost::asio::async_read(socket, buffer, boost::bind(&HandleRead,
boost::asio::placeholders::error_code,
boost::asio::placeholders::bytes_transferred,
the_context
));