代码之家  ›  专栏  ›  技术社区  ›  vladon

boost::asio::async_receive和套接字中的0字节

  •  3
  • vladon  · 技术社区  · 9 年前

    伪码

    boost::asio::streambuf my_buffer;
    boost::asio::ip::tcp::socket my_socket;
    
    auto read_handler = [this](const boost::system::error_code& ec, size_t bytes_transferred) {
                          // my logic
                        };
    
    my_socket.async_receive(my_buffer.prepare(512),
                            read_handler);
    

    当使用传统 recv 对于非阻塞套接字,当套接字中没有可读取的内容时,它返回-1。

    但使用 async_receive 不调用 read_handler 如果没有数据,它会无限等待。

    如何实现调用 读取处理器 具有 bytes_transferred == 0 (可能设置了错误代码)当套接字中没有可读取的内容时?

    ( async_read_some 具有相同的行为)。

    1 回复  |  直到 9 年前
        1
  •  5
  •   Tanner Sansbury    9 年前

    简而言之,在启动 async_receive() 操作,取消它。如果使用 boost::asio::error::operation_aborted 作为错误,则操作被阻止。否则,读取操作成功完成,并已从套接字读取数据,或者由于其他原因(例如远程对等方关闭连接)而失败。

    socket.async_receive(boost::asio::buffer(buffer), handler);
    socket.cancel();
    

    在异步操作的启动函数中,将尝试进行非阻塞读取。该行为在 async_receive() 文档:

    无论异步操作是否立即完成,[…]

    因此,如果操作在成功或错误的情况下立即完成,那么完成处理程序将准备好进行调用,并且不可取消。另一方面,如果操作将被阻止,那么它将被排入反应堆进行监控,在那里它将被取消。

    通过启用 non-blocking 模式。当套接字设置为非阻塞时,将阻塞的同步操作将失败 boost::asio::error::would_block .

    socket.non_blocking(true);
    auto bytes_transferred = socket.receive(
        boost::asio::buffer(buffer), 0 /* flags */, error);
    

    下面是一个完整的示例 demonstrating 这些行为:

    #include <array>
    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    
    // This example is not interested in the handlers, so provide a noop function
    // that will be passed to bind to meet the handler concept requirements.
    void noop() {}
    
    void print_status(
      const boost::system::error_code& error,
      std::size_t bytes_transferred)
    {
      std::cout << "error = (" << error << ") " << error.message() << "; "
                   "bytes_transferred = " << bytes_transferred
                << std::endl;
    }
    
    int main()
    {
      using boost::asio::ip::tcp;
    
      // Create all I/O objects.
      boost::asio::io_service io_service;
      tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
      tcp::socket socket1(io_service);
      tcp::socket socket2(io_service);
    
      // Connect the sockets.
      acceptor.async_accept(socket1, boost::bind(&noop));
      socket2.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
      io_service.run();
      io_service.reset();
    
      std::array<char, 512> buffer;
    
      // Scenario: async_receive when socket has no data.
      //   Within the intiating asynchronous read function, an attempt to read
      //   data will be made.  If it fails, it will be added to the reactor,
      //   for monitoring where it can be cancelled.
      {
        std::cout << "Scenario: async_receive when socket has no data" 
                  << std::endl;
        socket1.async_receive(boost::asio::buffer(buffer), &print_status);
        socket1.cancel();
        io_service.run();
        io_service.reset();
      }
    
      // Scenario: async_receive when socket has data.
      //   The operation will complete within the initiating function, and is
      //   not available for cancellation.
      {
        std::cout << "Scenario: async_receive when socket has data" << std::endl;
        boost::asio::write(socket2, boost::asio::buffer("hello"));
        socket1.async_receive(boost::asio::buffer(buffer), &print_status);
        socket1.cancel();
        io_service.run();
      }
      // One can also get the same behavior with synchronous operations by
      // enabling non_blocking mode.
      boost::system::error_code error;
      std::size_t bytes_transferred = 0;
      socket1.non_blocking(true);
    
      // Scenario: non-blocking synchronous read when socket has no data.
      {
        std::cout << "Scenario:  non-blocking synchronous read when socket"
                     " has no data." << std::endl;
        bytes_transferred = socket1.receive(
            boost::asio::buffer(buffer), 0 /* flags */, error);
        assert(error == boost::asio::error::would_block);
        print_status(error, bytes_transferred);
      }
    
      // Scenario: non-blocking synchronous read when socket has data.
      {
        std::cout << "Scenario:  non-blocking synchronous read when socket"
                     " has data." << std::endl;
        boost::asio::write(socket2, boost::asio::buffer("hello"));
        bytes_transferred = socket1.receive(
            boost::asio::buffer(buffer), 0 /* flags */, error);
        print_status(error, bytes_transferred);
      }
    }
    

    输出:

    Scenario: async_receive when socket has no data
    error = (system:125) Operation canceled; bytes_transferred = 0
    Scenario: async_receive when socket has data
    error = (system:0) Success; bytes_transferred = 6
    Scenario:  non-blocking synchronous read when socket has no data.
    error = (system:11) Resource temporarily unavailable; bytes_transferred = 0
    Scenario:  non-blocking synchronous read when socket has no data.
    error = (system:0) Success; bytes_transferred = 6