2017-08-16 114 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,除非您絕對需要利用其動態特性。