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();
}
};