【问题标题】:boost::asio::async_receive and 0 bytes in socketboost::asio::async_receive 和套接字中的 0 个字节
【发布时间】:2015-08-11 10:53:52
【问题描述】:

伪代码

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,而是无限等待。

当没有任何东西可以从套接字读取时,如何实现这样一个逻辑(异步)调用read_handlerbytes_transferred == 0(可能设置了错误代码)?

async_read_some 具有相同的行为)。

【问题讨论】:

  • 当没有可读取的内容时返回 -1 errno == EAGAIN/EWOULDBLOCK。零应表示对等方已断开连接。
  • @EJP 是的,recv 就像你说的那样。 async_receive怎么办?
  • 除非另有说明,否则我希望它的行为方式相同。
  • @EJP 实际上 read_handler 如果没有任何内容可以从套接字读取,则不会调用。它的作用与recv 不同。它无限地等待。今后,请在发表评论前阅读问题。

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


【解决方案1】:

简而言之,在启动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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2011-10-15
    • 2011-12-05
    • 2012-11-10
    相关资源
    最近更新 更多