I have a class that looks like this:
class MyConnector : public boost::noncopyable, public boost::enable_shared_from_this<MyConnector>
{
public:
typedef MyConnector this_type;
boost::asio::ip::tcp::socket _plainSocket;
boost::shared_ptr<std::vector<uint8_t>> _readBuffer;
// lot of obvious stuff removed....
void readProtocol()
{
_readBuffer = boost::make_shared<std::vector<uint8_t>>(12, 0);
boost::asio::async_read(_plainSocket, boost::asio::buffer(&_readBuffer->at(0), 12),
boost::bind(&this_type::handleReadProtocol, shared_from_this(),
boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error));
}
void handleReadProtocol(size_t bytesRead,const boost::system::error_code& error)
{
// handling code removed
}
};
This class instance is generally waiting to receive 12 bytes protocol, before trying to read the full message. However, when I try to cancel this read operation and destroy the object, it doesn't happen. When I call _plainSocket.cancel(ec), it doesn't call handleReadProtocol with that ec. Socket disconnects, but the handler is not called.
boost::system::error_code ec;
_plainSocket.cancel(ec);
And the shared_ptr of MyConnector object that was passed using shared_from_this() is not released. The object remains like a zombie in the heap memory. How do I cancel the async_read() in such a way that the MyConnector object reference count is decremented, allowing the object to destroy itself?
Two things: one, in handleReadProtocol, make sure that, if there is an error, that readProtocol is not called. Canceled operations still call the handler, but with an error code set.
Second, asio recommends shutting down and closing the socket if you're finished with the connection. For example:
asio::post([this] {
if (_plainSocket.is_open()) {
asio::error_code ec;
/* For portable behaviour with respect to graceful closure of a connected socket, call
* shutdown() before closing the socket. */
_plainSocket.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
if (ec) {
Log(fmt::format("Socket shutdown error {}.", ec.message()));
ec.clear();
}
_plainSocket.close(ec);
if (ec)
Log(fmt::format("Socket close error {}.", ec.message()));
}
});
Related
I am trying to wrap my head around resource management in boost::asio. I am seeing callbacks called after the corresponding sockets are already destroyed. A good example of this is in the boost::asio official example: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/example/cpp11/chat/chat_client.cpp
I am particularly concerned with the close method:
void close()
{
io_service_.post([this]() { socket_.close(); });
}
If you call this function and afterwards destruct chat_client instance that holds socket_, socket_ will be destructed before the close method is called on it. Also any pending async_* callbacks can be called after the chat_client has been destroyed.
How would you correctly handle this?
You can do socket_.close(); almost any time you want, but you should keep in mind some moments:
If you have threads, this call should be wrapped with strand or you can crash. See boost strand documentation.
Whenever you do close keep in mind that
io_service can already have queued handlers. And they will be called anyway with old state/error code.
close can throw an exception.
close does NOT includes ip::tcp::socket destruction. It
just closes the system socket.
You must manage object lifetime
yourself to ensure objects will be destroyed only when there is no
more handlers. Usually this is done with enable_shared_from_this
on your Connection or socket object.
Invoking socket.close() does not destroy the socket. However, the application may need to manage the lifetime of objects for which the operation and completion handlers depend upon, but this is not necessarily the socket object itself. For instance, consider a client class that holds a buffer, a socket, and has a single outstanding read operation with a completion handler of client::handle_read(). One can close() and explicitly destroy the socket, but the buffer and client instance must remain valid until at least the handler is invoked:
class client
{
...
void read()
{
// Post handler that will start a read operation.
io_service_.post([this]() {
async_read(*socket, boost::asio::buffer(buffer_);
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
});
}
void handle_read(
const boost::system::error_code& error,
std::size_t bytes_transferred
)
{
// make use of data members...if socket_ is not used, then it
// is safe for socket to have already been destroyed.
}
void close()
{
io_service_.post([this]() {
socket_->close();
// As long as outstanding completion handlers do not
// invoke operations on socket_, then socket_ can be
// destroyed.
socket_.release(nullptr);
});
}
private:
boost::asio::io_service& io_service_;
// Not a typical pattern, but used to exemplify that outstanding
// operations on `socket_` are not explicitly dependent on the
// lifetime of `socket_`.
std::unique_ptr<boost::asio::socket> socket_;
std::array<char, 512> buffer_;
...
}
The application is responsible for managing the lifetime of objects upon which the operation and handlers are dependent. The chat client example accomplishes this by guaranteeing that the chat_client instance is destroyed after it is no longer in use, by waiting for the io_service.run() to return within the thread join():
int main(...)
{
try
{
...
boost::asio::io_service io_service;
chat_client c(...);
std::thread t([&io_service](){ io_service.run(); });
...
c.close();
t.join(); // Wait for `io_service.run` to return, guaranteeing
// that `chat_client` is no longer in use.
} // The `chat_client` instance is destroyed.
catch (std::exception& e)
{
...
}
}
One common idiom is to managing object lifetime is to have the I/O object be managed by a single class that inherits from enable_shared_from_this<>. When a class inherits from enable_shared_from_this, it provides a shared_from_this() member function that returns a valid shared_ptr instance managing this. A copy of the shared_ptr is passed to completion handlers, such as a capture-list in lambdas or passed as the instance handle to bind(), causing the lifetime of the I/O object to be extended to at least as long as the handler. See the Boost.Asio asynchronous TCP daytime server tutorial for an example using this approach.
Boost's official site socket::close() function, see the description
"This function causes all outstanding asynchronous connect, send and receive operations to finish immediately, and the handlers for cancelled operations will be passed the boost::asio::error::operation_aborted error."
But strangely, when i call chat_session::close(), socket::close() passing ERROR_CONNECTION_ABORTED (1236) error instead of boost::asio::error::operation_aborted (995).
Why is this happens?
here is my chat_session class.
class chat_session
: public boost::enable_shared_from_this<chat_session>
{
public:
chat_session(boost::asio::io_service& io, chat_server* room)
: m_sock(io), m_room(room)
{
}
~chat_session()
{
}
void start()
{
m_room.join(shared_from_this());
m_sock.async_read_some(
boost::asio::buffer(m_recv_data),
boost::bind(&chat_session::handle_read, shared_from_this(),
boost::asio::placeholders::error));
}
void close()
{
// closing socket. chat_session::handle_read will receive
// boost::asio::error::operation_aborted error.
m_sock.close();
}
boost::asio::ip::tcp::socket& socket()
{
return m_sock;
}
private:
void handle_read(const boost::system::error_code& error)
{
if (!error)
{
printf("RECV -> %s.\n", m_recv_data);
m_sock.async_read_some(
boost::asio::buffer(m_recv_data),
boost::bind(&chat_session::handle_read, shared_from_this(),
boost::asio::placeholders::error));
}
else
{
// when i call chat_session::close(),
// ERROR_CONNECTION_ABORTED (1236) error occurred
// instead of boost::asio::error::operation_aborted error over here
...
m_room.leave(shared_from_this());
}
}
boost::asio::ip::tcp::socket m_sock;
chat_room& m_room;
char m_recv_data[50];
};
Try calling shutdown on the socket first before you close it, as the boost basic_stream_socket::close documentation specifies in the Remarks here:
Remarks
For portable behaviour with respect to graceful closure of a connected socket, call shutdown() before closing the socket.
Try something like the following in your close function:
m_sock.shutdown(boost::asio::ip::tcp::socket::shutdown_receive);
m_sock.close();
If you want to shut down both send and receive, use "shutdown_both" instead of "shutdown_receive".
Funny enough, I've seen this error happen on Windows but not Linux when using an implementation without the call to shutdown.
I am implementing something looks like a HTTP server, the design is: for a already established connection, I want to reuse it for several requests, so I start another reading task with async_read on it when a request is finished, and also start a deadline_timer. If there is no input in 60 seconds, the timer will be triggered and the connection will be destructed. The thing that annoys me is that before the invocation of the connection's destructor, the callback we set to async_read will be invoked.
So, my question is, is there any way to cancel the pending reading task, that is, destruct the connection without the callback function invoked?
If the generic description above is not clear, the detail work flow is as below(code is attached at the bottom):
cleanup() is called when a request finished;
start the timer and another reading task in cleanup();
if time is out, HandleTimeout() is called, and it calls stop();
in stop(), do the clean work, and after it, the connection instance will be destructed.
but, after step 4, the callback() function will be called, which is registered in AsyncRead(), so, is there any way to cancel the invocation of callback()?
code:
class Connection : public boost::enable_shared_from_this<Connection>,
private boost::noncopyable {
public:
typedef Connection this_type;
void cleanup() {
timer_.expires_from_now(boost::posix_time::seconds(kDefaultTimeout));
timer_.async_wait(boost::bind(&this_type::HandleTimeout,
shared_from_this(),
boost::asio::placeholders::error));
AsyncRead();
}
void AsyncRead() {
boost::asio::async_read(*socket_, in_, boost::asio::transfer_at_least(1),
boost::bind(&this_type::callback,
shared_from_this(),
boost::asio::placeholders::error));
}
void callback(const boost::system::error_code& e) {
// ...
}
void HandleTimeout(const boost::system::error_code& e) {
if(e == boost::asio::error::operation_aborted)
LDEBUG << "The timeout timer is cancelled.";
else if(e)
LERROR << "Error occurred with the timer, message: " << e.message();
else if(timer_.expires_at()
<= boost::asio::deadline_timer::traits_type::now()) {
LDEBUG << "Connection timed out, close it.";
stop();
}
}
virtual void stop() {
connected_ = false;
socket_->close();
connection_manager_.stop(shared_from_this());
}
private:
// ...
boost::asio::deadline_timer timer_;
};
There is no clean way to accomplish this. The only way to guarantee that ready-to-run handlers, such as Connection::callback(), will not be invoked is to either:
Stop processing the io_service event loop.
Destroy the io_service, as the io_service's destructor will cause all outstanding handlers to be destroyed.
In the example code, consider returning in Connection::callback() if the socket is no longer open:
void callback(const boost::system::error_code& error)
{
if (!socket_.is_open()) return;
// ...
}
Also note that the error_code argument is not enough to deduce whether the timeout has occurred. It is possible that Connection::callback() is queued for invocation with an error_code of boost::system::errc::success when socket::close() is invoked. Hence, there are no operations to cancel.
I want to create an autonomous thread devoted only to receive data from an UDP socket using boost libraries (asio). This thread should be an infinite loop triggered by some data received from the UDP socket. In my application I need to use an asynchronous receive operation.
If I use the synchronous function receive_from everything works as expected.
However if I use async_receive_from the handler is never called. Since I use a semaphore to detect that some data have been received, the program locks and the loop is never triggered.
I have verified (with a network analyzer) that the sender device properly sends the data on the UDP socket.
I have isolated the problem in the following code.
#include <boost\array.hpp>
#include <boost\asio.hpp>
#include <boost\thread.hpp>
#include <boost\interprocess\sync\interprocess_semaphore.hpp>
#include <iostream>
typedef boost::interprocess::interprocess_semaphore Semaphore;
using namespace boost::asio::ip;
class ReceiveUDP
{
public:
boost::thread* m_pThread;
boost::asio::io_service m_io_service;
udp::endpoint m_local_endpoint;
udp::endpoint m_sender_endpoint;
udp::socket m_socket;
size_t m_read_bytes;
Semaphore m_receive_semaphore;
ReceiveUDP() :
m_socket(m_io_service),
m_local_endpoint(boost::asio::ip::address::from_string("192.168.0.254"), 11),
m_sender_endpoint(boost::asio::ip::address::from_string("192.168.0.11"), 5550),
m_receive_semaphore(0)
{
Start();
}
void Start()
{
m_pThread = new boost::thread(&ReceiveUDP::_ThreadFunction, this);
}
void _HandleReceiveFrom(
const boost::system::error_code& error,
size_t received_bytes)
{
m_receive_semaphore.post();
m_read_bytes = received_bytes;
}
void _ThreadFunction()
{
try
{
boost::array<char, 100> recv_buf;
m_socket.open(udp::v4());
m_socket.bind(m_local_endpoint);
m_io_service.run();
while (1)
{
#if 1 // THIS WORKS
m_read_bytes = m_socket.receive_from(
boost::asio::buffer(recv_buf), m_sender_endpoint);
#else // THIS DOESN'T WORK
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
/* The program locks on this wait since _HandleReceiveFrom
is never called. */
m_receive_semaphore.wait();
#endif
std::cout.write(recv_buf.data(), m_read_bytes);
}
m_socket.close();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
};
void main()
{
ReceiveUDP receive_thread;
receive_thread.m_pThread->join();
}
A timed_wait on the semaphore is to be preferred, however for debug purposes I have used a blocking wait as in the code above.
Did I miss something? Where is my mistake?
Your call to io_service.run() is exiting because there is no work for the io_service to do. The code then enters the while loop and calls m_socket.async_receive_from. At this point the io_service is not running ergo it never reads the data and calls your handler.
you need to schedule the work to do before calling io_service run:
ie:
// Configure io service
ReceiveUDP receiver;
m_socket.open(udp::v4());
m_socket.bind(m_local_endpoint);
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
The handler function will do the following:
// start the io service
void HandleReceiveFrom(
const boost::system::error_code& error,
size_t received_bytes)
{
m_receive_semaphore.post();
// schedule the next asynchronous read
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
m_read_bytes = received_bytes;
}
Your thread then simply waits for the semaphore:
while (1)
{
m_receive_semaphore.wait();
std::cout.write(recv_buf.data(), m_read_bytes);
}
Notes:
Do you really need this additional thread? The handler is completely asynchronous, and boost::asio can be used to manage a thread pool (see: think-async)
Please do not use underscores followed by a capitol letter for variable / function names. They are reserved.
m_io_service.run() returns immediately, so noone dispatches completion handlers. Note that io_service::run is a kind of "message loop" of an asio-based application, and it should run as long as you want asio functionality to be available (this's a bit simplified description, but it's good enough for your case).
Besides, you should not invoke async.operation in a loop. Instead, issue subsequent async.operation in the completion handler of the previous one -- to ensure that 2 async.reads would not run simultaniously.
See asio examples to see the typical asio application design.
I am receiving this error message
"The I/O operation has been aborted because of either a thread exit or an application request"
when using boost::asio::socket::async_read_some()
What does the error mean? What should I be looking for?
Here is the relevant code:
void tcp_connection::start()
{
printf("Connected to simulator\n");
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void tcp_connection::read_sim_handler(
const boost::system::error_code& error, // Result of operation.
std::size_t len ) // Number of bytes read.
{
try {
if (error == boost::asio::error::eof) {
// Connection closed cleanly by peer.
printf("Sim connection closed\n");
return;
} else if (error) {
throw boost::system::system_error(error); // Some other error. if( ! error )
}
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
When I replace the call to async_read_some() with read_some() in the start() method, everything works fine ( except the server blocks waiting for a message! )
Following a comment i see that tcp_connection is going out of scope. I copied the code from http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/tutorial/tutdaytime3.html
which says this:
"We will use shared_ptr and enable_shared_from_this because we want to keep the tcp_connection object alive as long as there is an operation that refers to it."
I confess that I do not know what all that means. So I have broken it somehow?
Following further comments, the answer is
void tcp_connection::start()
{
printf("Connected to simulator\n");
socket_.async_read_some(boost::asio::buffer(myBuffer,256),
boost::bind(&tcp_connection::read_sim_handler,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Passing shared_from_this() rather than this employs the clever ( too clever? ) keep alive infrastructure established by the server code, even though the connection manager is not in scope, by normal means. For technical details, see comments under accepted answer.
Your tcp_connection object or your buffer object is likely going out of scope prior to the async operation completing.
Since your program is based on one of the tutorial examples, why don't you check out another of the examples that reads some data as well: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
The reason your class goes out of scope is that you are no longer using shared_from_this(). What this does is create a shared_ptr to your class that is stored by the bind handler. This means that the shared_ptr will keep your class alive until your handler is called.
This is also why you need to inherit from enable_shared_from_this.
The last shared_ptr that goes out of scope will delete your class instance.