I wrote an asynchronous SSL socket implementation using standalone asio and am struggling to get it to reconnect after a connection reset / close by the server. I am rather new to the asio library so please bear with me.
The thread that calls io_context::run remains blocked even after a disconnect because of the steady_timer. My close() logic is responsible for resetting the socket resources and is also responsible for trying to kill the timer. This is what my code looks like right now:
Creating my async job:
timer.async_wait(std::bind(&ssl_socket::heartbeat, this));
In my close() method:
timer.expires_at(std::chrono::steady_clock::now());
timer.cancel();
According to the boost docs, cancel() should:
Cancel any asynchronous operations that are waiting on the timer.
Perhaps I misinterpreting this but I would imagine this also cancels the asynchronous job that is bound to the io_context but it doesn't. io_context::run is never released and creates a deadlock.
This is what my timer handler looks like:
void ssl_socket::heartbeat() {
spdlog::get("console")->trace("heartbeat called");
if (connected_) {
write(heartbeat_token);
spdlog::get("console")->trace("heartbeat sent");
}
timer.expires_at(std::chrono::steady_clock::now() + std::chrono::seconds(heartbeat_interval));
timer.async_wait(std::bind(&ssl_socket::heartbeat, this));
}
I would like to keep handler away from having to validate if it should renew its timer and let the close() deal with that (if possible).
You are ignoring the error code.
According to the boost docs, cancel() should:
Cancel any asynchronous operations that are waiting on the timer.
This is a bit misleading. When you read the full description for the cancel function you'll see:
This function forces the completion of any pending asynchronous wait
operations against the timer. The handler for each cancelled operation
will be invoked with the boost::asio::error::operation_aborted error
code.
Which means, your handler will be called by the cancel function, and since your handler just re-sets the expiry-time and waits again, the cycle never ends. You need to check the error code and just break out of the cycle if it is set.
if(error) return;
Related
I 'm using boost beast 1.74.0. in another thread i try close the websocket but the code is broken at "acceptor.accept(socket, endpoint)" and i receive "Signal: SIG32 (Real-time event 32)" after call close.
Part from code to listen connection, What i need change to interrupt the accept correctly the service?
...
_acceptor = &acceptor;
_keepAlive = true;
while (_keepAlive) {
tcp::socket socket{ioc};
// Block until we get a connection
acceptor.accept(socket, endpoint);
// Launch the session, transferring ownership of the socket
std::thread(
&WebSocketServer::doSession,
std::move(socket),
this,
this,
getHeaderServer()
).detach();
}
close function call by another thread
void WebSocketServer::close() {
if (_acceptor != nullptr) this->close();
_keepAlive = false;
}
glibc uses SIG32 to signal the cancellation of threads created using the pthread library. Are you trying to use pthread_kill?
If not, you may be witnessing that only because you are running it under GDB. Which should be fixable by telling GDB to ignore that:
handle SIG32 nostop noprint
Finally to the original question:
there's interupption points in Boost Thread. They could help you iff you can switch to Boost Thread boost::thread instead of std::thread. Also, you have to change the thread's code to actually check for interruptions: https://www.boost.org/doc/libs/1_75_0/doc/html/thread/thread_management.html#thread.thread_management.tutorial.interruption
Since it actually sounds like you want to terminate the accept loop, why not "simply" cancel the acceptor? I'm not entirely sure this works with synchronous operations, but you could of course easily use an async accept.
Take care to synchronize access to the acceptor object itself. This means either run cancel on the same thread doing async_accept or from the same strand. By this point it surely sounds like it's easier to just do the whole thing asynchronously.
Which is the correct way to close and clean up a socket?
I have the io_service running in a secondary thread and I need to close the connection from the main thread:
void closeConnection()
{
ioc.post([&socket]() {
// Which ones do I have to call?
// In what order?
// What do they do?
//socket.cancel();
//socket.shutdown(asio::ip::tcp::socket::shutdown_both);
//socket.close();
//socket.release();
});
secondaryThread.join();
}
What is the difference between all these functions?
I've tried with this sequence ...
socket.cancel();
socket.close();
socket.release();
and seems to close up the connection without errors but takes too much time (about 5-10 seconds), so I guess I'm doing something wrong.
I presume you're talking about a graceful close, i.e. read/write any outstanding data, then close the socket.
The proper steps are:
Call shutdown() to indicate that you will not write any more data to the socket.
Continue to (async-) read from the socket until you get either an error or the connection is closed.
Now close() the socket (in the async read handler).
If you don't do this, you may end up closing the connection while the other side is still sending data. This will result in an ungraceful close.
cancel() and release() aren't the calls you use in a normal flow:
cancel() cancels all outstanding asynchronous operations.
release() releases ownership of the underlying native socket.
I am using boost::asio to transfer data to & fro from client to server. I have a reader thread on client side to read data received on the socket on client side. Please note that I am using boost::asio::read on client side & boost::asio::writeon server side.
Not using async_read or async_write. Everything works great.
However when I close my application, 2 out 10 times the app does not cleanly tear down or close properly. It gets hung while closing down The issue is the following:
My closing function gets called when destructors get called during my app's close down. Following is the code of the Close function:
socket.cancel();
socket.close();
boost::system::error_code ec;
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
The problem is that the boost::asio::read call does not return when it does not get any data & keeps waiting on it. This should be fine as long as I can cancel it. I am trying to do a socket.cancel on it to cancel all read operations while exiting.
However, it doesn't seems to work. I read in some forums that socket.cancel only cancels async_read operations. Is it so ? Then what is the way to cancel a boost::asio::read` operation when my app needs to exit ?
That's the nature of blocking IO.
Indeed socket.cancel() (or even io_service::stop()) will not work on synchronous operations.
The only way to interrupt this is to use socket-level timeouts (but Asio doesn't expose that) or to use asynchronous signals (e.g. pressing Ctrl-C in a terminal sends the child process a SIGINT).
I've previously created a poor-man's wrapper if you insist on running single operations with a timeout:
boost::asio + std::future - Access violation after closing socket
boost::system::error_code _error_code;
client_socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both, _error_code);
Above code help me close sync read immediately.
And sync read wiil return with error code: boost::asio::error::eof
I wonder why your code socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); did not work.
Maybe you should try again.
The error is due to the call to socket.close() before the call to socket.shutdown(). If you close a socket while there is a pending synchronous read(), you will occasionally get that error. It is really due to an expected data race in the underlying asio socket code.
Try removing the socket.close() call. Assuming your socket is wrapped in some kind of shared_ptr, you can let the socket destructor close the underlying socket.
You will still want to call socket.cancel() and socket.shutdown() explicitly in your use case in order to cancel outstanding operations.
I am trying to understand the difference between io_service's poll()/poll_one() and run()/run_one(). The difference as stated in the documentation is that poll() executes ready handlers as opposed to run() which executes any handler.
But nowhere in the boost documentation could I find the definition of a 'ready handler'.
A valid answer to this question is one able to show, preferably with a code example, the difference between a ready and non-ready handler and the difference between how poll() and run() executes it.
Thanks.
A "ready handler" is a handler that is ready to be executed. If you have issued an asynchronous call, it gets executed in the background and its handler becomes ready when the async call is done. Before that, the handler is pending, but not ready.
poll_one executes one ready handler if there is any.
poll executes all ready handlers, but not the pending. Both poll versions return immediately after the execution of the handlers.
run_one executes a ready handler if there is one, if not it waits for the first pending handler to become ready, meaning it blocks.
run executes and waits, until there are neither ready nor pending handlers. After it returns, the io_servie is in stopped state.
See also Boost::Asio : io_service.run() vs poll() or how do I integrate boost::asio in mainloop
int main()
{
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait([](const boost::system::error_code& err)
{ std::cout << (err ? "error" : "okay")
;});
//io_service.poll_one();
io_service.run_one();
}
If you use io_service.poll_one(); you will most likely not see any output because the timer has not elapsed yet. ready handler simply means a handle that is ready to run (such as when a timer elapses or an operation finishes, etc.). However, if you use io_service.run_one(); this call will block until the timer finishes and execute the handler.
This is my server code:
socket_.async_read_some(boost::asio::buffer(data_read.data(), Message::header_length),
boost::bind(&TcpConnection::handle_read_header, shared_from_this(),
boost::asio::placeholders::error));
If i write a the the following code in a loop
boost::thread::sleep(boost::posix_time::seconds(2));
in the 'handle_read_header' function which is called by the above 'async_read_some' the whole thread is waiting till the sleep end. So when another request comes in it is not handled until the sleep finishes. Isn't is suppose to asynchronously handles each requests? I am new to boost and C++. Please let me know if i have mentioned anything wrong.
Read scheduled with async_read_some is realized in the thread which called io_service::run().
If you have only one thread it will wait for completing one read handler, before starting another one.
You can make a thread pool, by running more threads with io_service::run() or make the execution of read handler shorter.