Is it safe to have fire and forget approach to udp socket in boost::asio?
so e.g if i have such code
socket.async_send(buffer(somedata1),write_handler);
socket.async_send(buffer(somedata2),write_handler);
socket.async_send(buffer(somedata3),write_handler);
socket.async_send(buffer(somedata4),write_handler);
am i guaranteed that this will not fail - that means that at the receiving endpoint i will get 4 packets containing somedata1,somedata2,somedata3,somedata4 ?
No, it most certainly isn't safe, none of the asio async_* functions are documented to be "fire and forget".
The boost asio reference for the basic_datagram_socket::async_send buffers 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.”
If you need a "fire and forget" approach, then you'll need to a class to manage your connection and buffer the packets for you. Here's an example using a deque to buffer the packets:
class Connection : public boost::enable_shared_from_this<Connection>
{
boost::asio::ip::udp::socket socket_;
std::deque<std::vector<char> > tx_queue_;
/// transmit the packet at the head of the queue
void transmit()
{
socket_.async_send(
boost::asio::buffer(&tx_queue_.front()[0], tx_queue_.front().size()),
boost::bind(&Connection::write_callback,
boost::weak_ptr<Connection>(shared_from_this()),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
/// The function called whenever a write event is received.
void write_handler(boost::system::error_code const& error,
size_t /* bytes_transferred */)
{
tx_queue_.pop_front();
if (error)
; // handle the error, it may be a disconnect.
else
if (!tx_queue_.empty())
transmit();
}
/// Static callback function.
/// It ensures that the object still exists and the event is valid
/// before calling the write handler.
static void write_callback(boost::weak_ptr<Connection> ptr,
boost::system::error_code const& error,
size_t bytes_transferred)
{
boost::shared_ptr<Connection> pointer(ptr.lock());
if (pointer && (boost::asio::error::operation_aborted != error))
pointer->write_handler(error, bytes_transferred);
}
/// Private constructor to enusure the class is created as a shared_ptr.
explicit Connection(boost::asio::io_service& io_service) :
socket_(io_service),
tx_queue_()
{}
public:
/// Factory method to create an instance of this class.
static boost::shared_ptr<Connection> create(boost::asio::io_service& io_service)
{ return boost::shared_ptr<Connection>(new Connection(io_service)); }
/// Destructor, closes the socket to cancel the write callback
/// (by calling it with error = boost::asio::error::operation_aborted)
/// and free the weak_ptr held by the call to bind in the transmit function.
~Connection()
{ socket_.close(); }
/// add the packet to the end of the queue and send it ASAP.
#if defined(BOOST_ASIO_HAS_MOVE)
void send(std::vector<char>&& packet)
#else
void send(const std::vector<char>& packet)
#endif
{
bool queue_empty(tx_queue_.empty());
tx_queue_.push_back(packet);
if (queue_empty)
transmit();
}
};
There is no guarantee simply because the underlying protocol makes no guarantee.
As long as the underlying memory provided to buffer during socket.async_send()
remains valid until the handler is called, and no other thread is making concurrent calls to socket
, then the criteria for socket.async_send()
has been satisfied and it should be safe.
For implementation details, the basic_datagram_socket::async_send()
will have its service create a single non-composed operation (reactive_socket_send_op
or win_iocp_socket_send_op
). The service will then have its reactor
start the operation. Some reactor implementations may attempt to run the operation immediately; otherwise, the operation is added to a queue specific to the socket file descriptor based on the operation type (read or write). The operation queues allow for multiple read or write operations to be outstanding for a given file descriptor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With