为异步发送缓冲区保留内存(增强的asio套接字) [英] Reserving memory for asynchronous send buffers (boost asio sockets)
问题描述
我正在尝试将即弃型UDP发送功能的实现方式从同步更改为异步.
I'm trying to change the implementation of a fire-and-forget UDP send-function from being synchronous to asynchronous.
当前的简化同步功能如下:
The current simplified synchronous function looks like this:
ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) {
return mPSocket->send_to(boost::asio::buffer(buffer, bufferSize), mOutputEndpoint);
}
我已经设置了thread_group
并将io_service::run()
设置为可以使用它.但是,问题是我无法保证在此调用完成之后buffer
将存在.我需要存储缓冲区的内容,然后知道什么时候空闲,以便以后可以重用或删除它.以下内容很简单,但是如果我触发了两个send_to
调用,那么我无法保证handle_send
会以相同的顺序被调用,并且我可能会pop
仍然需要一些东西!
I've got a thread_group
set up and io_service::run()
is set to use it. However, the problem is that I have no guarantee that buffer
will exist after this call has completed. I need to store the contents of the buffer and then know when it is free so that I can re-use it later or delete it. The following is simple, but if I fire off two send_to
calls, then I have no guarantee that the handle_send
will be called in the same order and I might pop
something that is still needed!
ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) {
boost::asio::mutable_buffer b1 = boost::asio::buffer(buffer,bufferSize);
mMutex.lock();
mQueue.push(b1);
mPSocket->async_send_to(mQueue.back(), mOutputEndpoint,
boost::bind(&UDPTransport::handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
mMutex.unlock();
return bufferSize;
}
void UDPTransport::handle_send(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
mMutex.lock();
mQueue.pop();
mMutex.unlock();
}
什么是存储异步缓冲区,然后在不再需要它时清理它的好方法?
What's a good way to store an asynchronous buffer, then clean it up once it's no longer needed?
在线阅读下面可能是一种更简单的方法,但是我不知道我是否相信它.为什么共享指针会决定在调用处理程序之后才取消自身分配?
Reading online an even simpler way may be below, but I don't know if I trust it. Why would a shared pointer decide to not de-allocate itself until after the handler has been called?
ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize)
{
auto buf = std::make_shared<std::string>(buffer, bufferSize);
mPSocket->async_send_to(boost::asio::buffer(*buf), mOutputEndpoint,
boost::bind(&UDPTransport::handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
return bufferSize;
}
推荐答案
我通常要做的是将其包装在继承自std :: enable_shared_from_this<>的类中,如下所示:
What I usually do is to wrap it in a class that inherits from std::enable_shared_from_this<> something along the following lines:
class Sender : public std::enable_shared_from_this<Sender> {
public:
using CompletionHandler =
std::function<void(const boost::system::error_code& ec,
size_t bytes_transferred,
std::shared_ptr<Sender> sender)>;
~Sender() = default;
template<typename... Args>
static std::shared_ptr<Sender> Create(Args&&... args) {
return std::shared_ptr<Sender>(new Sender(std::forward<Args>(args)...));
}
void AsyncSendTo(const char* buffer, size_t buffer_size,
CompletionHandler completion_handler) {
data_.append(buffer, buffer_size);
socket.async_send_to(
boost::asio::buffer(data_), endpoint_,
[self = shared_from_this(),
completion_handler = std::move(completion_handler)]
(const boost::system::error_code& ec,
size_t bytes_transferred) mutable {
completion_handler(ec, bytes_transferred, std::move(self));
});
}
private:
Sender() = default;
Sender(const Sender&) = delete;
Sender(Sender&&) = delete;
Sender& operator=(const Sender&) = delete;
Sender& operator=(Sender&&) = delete;
SocketType socket_;
EndpointType endpoint_;
std::string data_;
}
显然,您必须保证completion_handler
的寿命.但是除此之外,完成处理程序将在完成后返回一个有效的std::shared_ptr<Sender>
,并且您可以使用发件人携带的数据进行所需的任何操作.
Obviously, you have to guarantee the completion_handler
's lifetime. But other than that, the completion handler is gonna come back with a valid std::shared_ptr<Sender>
whenever it's done and you can do whatever you need with the data Sender carries.
在您发布的示例中,buf
将离开范围并在send_to
返回时被销毁,除非您首先在bind
中捕获了它.
In the example you posted, buf
would leave scope and get destroyed on send_to
return, unless you first captured it in bind
.
脚注1:可能需要删除那些std::move()
,这取决于您的编译器在lambda上是否兼容C ++ 14.
Footnote1: Those std::move()
s might need to be removed depending on whether your compiler is C++14 compatible when it comes to lambdas.
Footnote2:除非绝对需要利用它的动态特性,否则请远离bind
.
Footnote2: Stay away from bind
unless you absolutely need to exploit its dynamic nature.
这篇关于为异步发送缓冲区保留内存(增强的asio套接字)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!