C++ 为异步发送缓冲区保留内存(boost asio套接字)
我正在尝试将fire and forget UDP发送函数的实现从同步更改为异步 当前简化的同步功能如下所示:C++ 为异步发送缓冲区保留内存(boost asio套接字),c++,sockets,boost,boost-asio,C++,Sockets,Boost,Boost Asio,我正在尝试将fire and forget UDP发送函数的实现从同步更改为异步 当前简化的同步功能如下所示: ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) { return mPSocket->send_to(boost::asio::buffer(buffer, bufferSize), mOutputEndpoint); } 我已经设置了一个线程组,并且io\u service::
ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) {
return mPSocket->send_to(boost::asio::buffer(buffer, bufferSize), mOutputEndpoint);
}
我已经设置了一个线程组
,并且io\u service::run()
已设置为使用它。但是,问题是我不能保证在这个调用完成后,缓冲区将存在。我需要存储缓冲区的内容,然后知道它什么时候是空闲的,以便以后可以重复使用或删除它。下面很简单,但是如果我发出两个send\u-to
调用,那么我不能保证handle\u-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();
}
存储异步缓冲区,然后在不再需要时清理它的好方法是什么
下面可能是一个更简单的方法,但我不知道我是否相信它。为什么共享指针要在调用处理程序之前决定不取消自身分配
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;
}
ssize\u t udpttransport::发送到(const char*buffer,size\u t bufferSize)
{
自动buf=std::使_共享(缓冲区、缓冲区大小);
mPSocket->async_send_to(boost::asio::buffer(*buf),moutpundpoint,
boost::bind(&udpttransport::handle_send,这个,
boost::asio::占位符::错误,
boost::asio::占位符::字节(已传输);
返回缓冲区大小;
}
我通常做的是将其包装在一个继承自std::enable_shared_from_的类中,并遵循以下几行:
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_;
}
类发送方:公共std::从\u中启用\u共享\u{
公众:
使用CompletionHandler=
std::函数;
~Sender()=默认值;
模板
静态std::共享\u ptr创建(Args&&…Args){
返回std::shared_ptr(新发送方(std::forward(args)…);
}
void AsyncSendTo(常量字符*缓冲区,大小\u t缓冲区大小,
CompletionHandler(完成处理程序){
数据追加(缓冲区、缓冲区大小);
socket.async\u发送到(
boost::asio::缓冲区(数据),端点,
[self=shared_from_this(),
完成处理程序=std::move(完成处理程序)]
(const boost::系统::错误代码和ec,
大小(传输的字节数)可变{
完成处理程序(ec,已传输字节,std::move(self));
});
}
私人:
Sender()=默认值;
发送方(const Sender&)=删除;
发送者(发送者&)=删除;
发送方和运算符=(const Sender&)=删除;
发送方和运算符=(发送方和运算符)=删除;
插座式插座;
端点类型端点;
std::字符串数据;
}
显然,您必须保证完成处理程序的生命周期。但除此之外,无论何时完成,完成处理程序都会返回一个有效的std::shared_ptr
,您可以对数据发送器执行任何需要的操作
在您发布的示例中,buf
将离开作用域并在send_to
return上被销毁,除非您首先在bind
中捕获它
脚注1:可能需要删除那些std::move()
s,这取决于编译器是否与lambdas兼容
脚注2:除非您绝对需要利用其动态特性,否则请远离bind
。您链接的共享\u ptr解决方案可能是正确的,因为它在lambda中按值捕获共享的\u ptr。因此,一旦调用了处理程序,它就应该释放自己。但是,您的共享ptr代码不能做到这一点。