【问题标题】:boost asio multiple async_send on udp socket在 udp 套接字上提升 asio 多个 async_send
【发布时间】:2013-11-07 03:38:02
【问题描述】:

在 boost::asio 中使用 udp 套接字的方法是否安全?

例如,如果我有这样的代码

 socket.async_send(buffer(somedata1),write_handler);
 socket.async_send(buffer(somedata2),write_handler);
 socket.async_send(buffer(somedata3),write_handler);
 socket.async_send(buffer(somedata4),write_handler);

我保证这不会失败 - 这意味着在接收端点我将收到 4 个包含 somedata1、somedata2、somedata3、somedata4 的数据包?

【问题讨论】:

    标签: c++ sockets boost udp boost-asio


    【解决方案1】:

    不,它肯定不安全,没有 asio async_* 函数被记录为“即发即弃”。

    basic_datagram_socket::async_send 缓冲区的 boost asio 引用指出:“虽然缓冲区对象可能会根据需要被复制,但底层内存块的所有权由调用者保留,它必须保证它们在调用处理程序之前保持有效。”

    如果您需要“一劳永逸”的方法,那么您需要一个类来管理您的连接并为您缓冲数据包。这是一个使用双端队列缓冲数据包的示例:

    class Connection : public boost::enable_shared_from_this<Connection>
    {
      boost::asio::ip::udp::socket socket_;
      std::deque<std::vector<char> > tx_queue_;
    
      /// transmit the packet at the head of the queue
      void transmit()
      {
        socket_.async_send(
          boost::asio::buffer(&tx_queue_.front()[0], tx_queue_.front().size()),
          boost::bind(&Connection::write_callback,
                      boost::weak_ptr<Connection>(shared_from_this()),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));
      }
    
      /// The function called whenever a write event is received.
      void write_handler(boost::system::error_code const& error,
                         size_t /* bytes_transferred */)
      {
        tx_queue_.pop_front();
        if (error)
          ; // handle the error, it may be a disconnect.
        else
          if (!tx_queue_.empty())
            transmit();
      }
    
      /// Static callback function.
      /// It ensures that the object still exists and the event is valid
      /// before calling the write handler.
      static void write_callback(boost::weak_ptr<Connection> ptr,
                                 boost::system::error_code const& error,
                                 size_t bytes_transferred)
      {
        boost::shared_ptr<Connection> pointer(ptr.lock());
        if (pointer && (boost::asio::error::operation_aborted != error))
          pointer->write_handler(error, bytes_transferred);
      }
    
      /// Private constructor to enusure the class is created as a shared_ptr.
      explicit Connection(boost::asio::io_service& io_service) :
        socket_(io_service),
        tx_queue_()
      {}
    
    public:
    
      /// Factory method to create an instance of this class.
      static boost::shared_ptr<Connection> create(boost::asio::io_service& io_service)
      { return boost::shared_ptr<Connection>(new Connection(io_service)); }
    
      /// Destructor, closes the socket to cancel the write callback 
      /// (by calling it with error = boost::asio::error::operation_aborted)
      /// and free the weak_ptr held by the call to bind in the transmit function.
      ~Connection()
      { socket_.close(); }
    
      /// add the packet to the end of the queue and send it ASAP.
    #if defined(BOOST_ASIO_HAS_MOVE)
      void send(std::vector<char>&& packet)
    #else
      void send(const std::vector<char>& packet)
    #endif
      {
        bool queue_empty(tx_queue_.empty());
        tx_queue_.push_back(packet);
        if (queue_empty)
          transmit();
      }
    };
    

    【讨论】:

    • 谢谢@Igor,我不知道有任何async\_* 函数做到了这一点。您显然比我更了解 asio 内部结构。我编辑了我的答案,将行更改为 *documented* to be "fire and forget" 并参考了有关异步缓冲区的 asio 文档。所以这种方式是没有争议的,我们都是对的! ;)
    【解决方案2】:

    没有保证只是因为底层协议没有保证。

    只要在socket.async_send() 期间提供给缓冲区的底层内存在调用处理程序之前保持有效,并且没有其他线程对socket 进行并发调用,那么socket.async_send() 的条件就已经满足并且应该安全。


    对于实现细节,basic_datagram_socket::async_send() 将让它的服务创建一个非组合操作(reactive_socket_send_opwin_iocp_socket_send_op)。然后该服务将使用其reactor 启动操作。一些反应器实现可能会尝试立即运行操作;否则,该操作将根据操作类型(读取或写入)添加到特定于套接字文件描述符的队列中。操作队列允许对给定文件描述符进行多个未完成的读取或写入操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-20
      相关资源
      最近更新 更多