2017-08-16 48 views
1

我试图改变一个即发即热的UDP发送函数从同步到异步的实现。为异步发送缓冲区保留内存(升压asio套接字)

目前简易的同步功能看起来是这样的:

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; 
} 
+1

您链接的shared_ptr解决方案可能是正确的,因为它在lambda中通过值捕获shared_ptr。所以一旦处理程序被调用,它应该释放自己。然而,你的代码与shared_ptr不这样做。 –

回答

2

我最常做的就是把它包在从STD继承的类:: enable_shared_from_this <>沿以下的说法:

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>,并且您可以根据发件人携带的数据执行任何您需要的操作。

在您发布的示例中,buf将离开作用域,并在send_to返回时被销毁,除非您首先在bind中捕获到它。

脚注1:可能需要删除那些std::move(),具体取决于您的编译器是否与lambda表达式兼容。

脚注2:远离bind,除非您绝对需要利用其动态特性。

相关问题