Asio end socket functions: cancel, shutdown, close, release - c++

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.

Related

Properly killing an asio steady_timer after a disconnect

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;

how to cancel a `boost::asio::read` operation while it's waiting

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.

Trigger an EAGAIN on a socket receiving on another thread

Say that I have two threads, the main thread and a thread that is dedicated to continuously listening on a standard TCP socket. Now, say that at some point I want to shutdown everything. From the main thread, I would like to close the connection the listening thread is working on, then join the thread and end the program.
However, this is tricky, since I don't know how to make the listening thread return from the call to read. That call won't return unless something actually is received, and in principle I could be waiting for a long long time until the other endpoint decides to send me something.
When I was working with UDP sockets, I used to work around this problem by sending a packet on that port from my loopback interface, therefore triggering a return from recvfrom. However, this is terribly inelegant and it cannot be done on a TCP socket.
I know that another workaround could be to set a timeout with setsockopt: in this way I am guaranteed that the call will eventually return, but this is inelegant as well, and also quite inefficient since I could be waiting for several seconds before being able to join the thread.
So I was wondering if there is some way to trigger an EAGAIN on a socket read call, not unlike the one I would get on a timeout, so that on my main thread I could just call some force_returnon my socket descriptor and the call to read on the other thread would return?
I usually solve this problem by creating a pipe() and using select() in the reading thread. The reading thread must select on both the TCP socket and one end of the pipe. Whenever you want to shut down the reader, set a flag and write some data to the other end of the pipe.
Setup:
#include <unistd.h>
int signalPipe[2];
...
pipe(signalPipe);
Reader:
while(running)
{
FD_ZERO(&fds);
FD_SET(tcpSocket, &fds);
FD_SET(signalPipe[0], &fds);
select(max(tcpSocket, signalPipe[0]) + 1, &fds, NULL, NULL, NULL);
...
}
Other thread:
// We want to stop now.
running = false;
write(signalPipe[1], "foo", 3);

C++ Can I make boost::asio connection socket without timer?

I have a window server socket and a Linux client socket. Client connect to server and send a message. After, the server will call a external executable. The problem is : when server is not available, Client is blocking with timeout at connect function, But I don't want it. I hope if the connection is not made, client socket will be closed straight away.
Somebody can give me some advice?
Warning: Pseudo-Code ahead.
You can do that. But it is not as straight forward as you might hope.
You need to use async_connect() from your client to not block. Then you also need a deadline_timer set to whatever timeout you deem appropriate. Zero will not work, you need to give the async_connect() some time. But i guess one or two seconds should be fine.
The timers handler will then have to cancel() all async operations on the socket (you need to make sure that is only the connect, use more sockets if needed).
Mind the socket will not be closed by that. Ideally you will close it in the handler of the async_connect whenever the passed error_code indicates a negative result. For example, if it was canceled, the handler will be called with OPERATION_ABORTED as error_code.
Of course, if you check only for that, you could as well close() the socket in the timers handler after the cancel(). But that would leave you with an open socket whenever the async_connect failed for some other reason.
I would assume from your question that you want your socket to get closed whenever the async_connect() passes any error_code but SUCCESS. And SUCCESS is the only error_code implicitly converted to 0 when used as a boolean, so checking for that in your handler is easy. ^^
Do not forget to cancel the deadline_timer in the handler of the async_connect() and to make sure the timers handler was not called with OPERATION_ABORTED before it closes the socket. ^^

Cancel pending recv?

Hi I'm working on a networking project. I've a socket that is listening incoming data. Now I want to archive this: Socket will receive only 100 packets. And there is 3-4 clients. They are sending random data packets infinitely. I'll receive 100 packets and later I'll process them. After process I'll re-start receiving. But at this time there are some pending send() >> recv() operations. Now I want to cancel/discard pending recv operations. I think we'll recv datas and we'll not process them. Any other suggestions? (sorry for bad question composition)
Shutdown and close the connection. That will cancel everything immediately.
Better yet, rearchitect your application and network protocol so that you can reliably tell how much data to receive.
On Windows you can cancel outstanding receives using CancelIO, but that might result in lost data if the receive just happened to read something.
You can use select() or poll() loops.
you can use signal. recv() will return on receiving a signal so you can send a signal from another task to the task that blocks on recv(). But you need to make sure you don't specify SA_RESTART (see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html)
Read http://en.wikipedia.org/wiki/Asynchronous_I/O for more details
I would go with non-blocking sockets + cancellation socket.
You'll have to read into dedicated incremental buffer (as recv() may not receive all the data expected at once - this would be the case if you can only process full messages) and return to select()/poll() in your loop, where you can safely sit and wait for:
next data
next connection
cancellation event from a cancellation socket, to which your other thread will send a cancellation signal (some trivial send()).
UPD: the trivial event may be the number of the socket in the array or its handle - something to identify which one you'd like to cancel.