【问题标题】:Reserving memory for asynchronous send buffers (boost asio sockets)为异步发送缓冲区保留内存(提升 asio 套接字)
【发布时间】:2017-08-16 13:35:12
【问题描述】:

我正在尝试将即发即弃 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;
}

【问题讨论】:

  • 您链接的 shared_ptr 解决方案可能是正确的,因为它在 lambda 中按值捕获 shared_ptr。因此,一旦调用了处理程序,它就应该自行释放。但是你的 shared_ptr 代码并没有这样做。

标签: c++ sockets boost boost-asio


【解决方案1】:

我通常做的是将它包装在一个继承自 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&lt;Sender&gt;,您可以对 Sender 携带的数据做任何您需要的事情。

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

脚注 1:可能需要删除这些 std::move()s,具体取决于您的编译器在 lambda 方面是否与 C++14 兼容。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 2023-03-31
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 2018-11-08
    • 2023-03-25
    相关资源
    最近更新 更多