我试图将即发即弃的UDP发送功能的实现从同步更改为异步。
当前的简化同步函数如下所示:
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
仍需要的东西!
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();
}
什么是存储异步缓冲区的好方法,然后在不再需要它时将其清理干净?
Reading online更简单的方法可能在下面,但我不知道我是否相信它。为什么共享指针决定在调用处理程序之后才解除分配?
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;
}
答案 0 :(得分:2)
我通常做的是将它包装在一个继承自std :: enable_shared_from_this&lt;&gt;的类中。以下内容:
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>
,并且您可以使用Sender所携带的数据执行任何操作。
在您发布的示例中,buf
会在send_to
返回时留下范围并被销毁,除非您首先在bind
中捕获它。
脚注1:可能需要删除那些std::move()
,具体取决于编译器在lambdas方面是否与C ++ 14兼容。
脚注2:远离bind
,除非您绝对需要利用其动态性质。