What exactly does the destructor of boost::asio::ip::tcp::socket do? I can't tell, even after scouring Boost docs and source code, if I need to use
socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
socket->close();
before calling
delete socket;
Do I need to close the socket manually, or does the destructor handle this?
When a socket is destroyed, it will be closed as-if by socket.close(ec) during the destruction of the socket.
I/O objects, such as socket, derive from basic_io_object. Within the basic_io_object destructor, destroy() will be invoked on the I/O object's I/O service, passing in an instance of the implementation_type on which the I/O object's service will operate. In the case of socket, destroy() will be invoked on a type that fulfills the SocketService type requirement, closing the underlying socket. In the documentation below, a is an instance of a socket service class, and b is an instance of the implementation_type for the socket service class:
a.destroy(b):
[...] Implicitly cancels asynchronous operations, as if by calling a.close(b, ec).
a.close(b, ec):
If a.is_open() is true, causes any outstanding asynchronous operations to complete as soon as possible. Handlers for cancelled operations shall be passed the error code error::operation_aborted.
post: !a.is_open(b).
No you don't need to close it. Though it might be cleaner to do so, if you want to report any errors surrounding protocol shutdown.
The destructor just /appears/ to be empty, that's a good sign of Modern
C++:
http://en.cppreference.com/w/cpp/language/rule_of_three
Rule Of Zero
The answers have skipped over the issue of shutdown(). From the close() documentation, "For portable behaviour with respect to graceful closure of a connected socket, call shutdown() before closing the socket".
If deleting the socket does an implicit close, it seems that a call to shutdown() is still recommended before deleting it.
Related
Is the following code safe?
boost::asio::io_service io_service;
auto socket(new std::unique_ptr<boost::asio::tcp::socket>(io_service);
.
.
.
boost::asio::async_read(*socket, buffer, handler);
socket.reset();
Or do I need to wait for the handler to run before I am allowed to delete the socket?
Also will using socket->async_receive(...) instead make any difference
I am interested in this on both windows and linux.
Resetting the pointer calls the destructor on the socket. The destructor explicit cancels any async operations, so that is fine.
There's the issue with threading, though. You cannot invoke the destructor while another member of socket is being accessed because the type is not threadsafe.
The usual shutdown patterns are to /post/ operations on the IO objects (like socket) on their respective strand, and/or to stop() the service itself and wait for the service threads to return.
Also related: Why do we need to use boost::asio::io_service::work?
The extensive documentation helpfully says:
This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down or an error occurs.
Which leaves me with these questions:
Does this close the actual connection too?
If not, how do I do that?
Can I reuse the stream by calling boost::asio::connect(theSocket.lowest_layer(), ... again?
Under the covers, asio calls SSL_Shutdown() in the underlying OpenSSL library:
The documentation for which is here:
https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
... and contains the following ominous text:
The behaviour of SSL_shutdown() additionally depends on the underlying BIO.
However, my understanding of things is that the ssl::stream object is not a socket (by design) but more like a protocol layer that sits on top of a Stream-like object. Thus the asio::ssl layer has no knowledge of its next_layer() other than that it must support the free functions read,write, async_read and async_write. Certainly no knowledge about whether the layer supports the concept of shutdown() (or even close()).
Therefore I would expect the underlying socket, given by lowest_layer() to still be open when shutdown() returns. Furthermore, I would expect that shutdown() will not have been called on the lowest_layer(). You'll want to do this after ssl::shutdown returns, or in the handler passed to ssl::async_shutdown
From the standard SSL client example. Say I call this function.
boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
boost::bind(&SSLClient::handle_connect, this,
boost::asio::placeholders::error));
But then the function is called, and the program is connecting. I would like to cancel my request and stop the connection! How can I do that?
Special case: Say I have those objects in a thread. Is there a way to do it in this case?
Now if I try to do this, the program simply doesn't respond. I don't see a way to force it to stop!
There are several ways to achieve what you want ¹.
You could hard-stop the service (service.stop()). But this leaves you no control over all running operations. It's the "nuclear" approach, so to say.
The controlled way would be to call cancel()
Cancel all asynchronous operations associated with the socket.
socket_.cancel()
Now, you have the additional task of maintaining the lifetime of your connection object (presumably the this in your bound completion handler). A very common pattern to use is to make the connection class derive from enable_shared_from_this and bind the completion handler to shared_from_this() instead of just this.
That way, the shared connection object will automatically "go away" after the last pending async operation has been canceled, and you don't have to worry about leaking connection objects.
¹ short of exit, abort, quick_exit etc. :)
I tried looking through source but I cant navigate that much of a template code.
Basically: this is what documentation says (for close()):
Remarks
For portable behaviour with respect to graceful
closure of a connected socket, call shutdown() before closing the socket.
I can do that manually, but if possible it would be nice to rely on RAII.
So if I have socket going out of scope do I need to call shutdown() and close() on it, or it will be done automatically?
One can rely on the socket performing proper cleanup with RAII.
When an IO object, such as socket, is destroyed, its destructor will invoke destroy() on the IO object's service, passing in an instance of the implementation_type on which the IO object's service will operate. The SocketService requirements state that destroy() will implicitly cancel asynchronous operations as-if by calling the close() on the service, which has a post condition that is_open() returns false. Furthermore, the service's close() will cause outstanding asynchronous operations to complete as soon as possible. Handlers for cancelled operations will be passed the error code boost::asio::error::operation_aborted, and scheduled for deferred invocation within the io_service. These handlers are removed from the io_service if they are either invoked from a thread processing the event loop or the io_service is destroyed.
In the following code:
tcp::socket socket(io_service);
tcp::endpoint ep(boost::asio::ip::address::from_string(addr), i);
socket.async_connect(ep, &connect_handler);
socket.close();
is it correct to close the socket object, or should I close it only in the connect_handler(), resort to shared_ptr to prolong the life of of the socket object?
Thanks.
Closing the socket isn't much of an issue, but the socket being destructed and deallocated is. One way to deal with it is to just make sure the socket outlives the io_service where work is being done. In other words, you just make sure to not delete it until after the io_service has exited. Obviously this won't work in every situation.
In a variety of conditions it can be difficult to impossible to tell when all work is really done on the socket when it's active within the io_service, and ASIO doesn't provide any mechanism to explicitly remove or disconnect the object's callbacks so they don't get called. So you should consider holding the connection in a shared_ptr, which will keep the connection object until the last reference inside the io_service has been released.
Meanwhile your handler functors should handle all possible errors passed in, including the connection being destroyed.
It is safe. The connect_handler will give you ec == boost::asio::error::connection_aborted. Of course, you need to do io_service.run() for the handler to be invoked.
As already answered by Chila, it's safe to close the socket whenever you want. If the socket in question has an outstanding operation at the time, the handler/callback will be invoked to notify you've cancelled the operation. That's where connection_aborted shows up.
As for your question about shared_ptr, I consider it a big win if you have another thread or other objects referencing your sockets, however, it isn't required in many cases. All you have to do is to dynamically allocate them, and deallocate when they're no longer needed. Of course, if you have other objects or threads referencing your socket, you must update them prior to delete/dealloc. Doing so, you avoid invalid memory access because the object pointed by them no longer exists (see dangling pointer).