首次使用Boost :: asio :: async_read读取EOF

时间:2014-05-05 13:12:54

标签: c++ asynchronous boost-asio

我使用Boost Asio实现了一个非常简单的协议。我发送一个简单的查询,我得到一个可变长度的响应。异步发送似乎工作,并调用写入处理程序。 由于我不知道响应有多长,我首先阅读固定的8字节标题。这将始终存在,并包含剩余部分的长度。相关电话:

char input[256]; // Large enough to also hold the variable part.
async_read(socket, buffer(input), transfer_at_least(8), callback);

callback中,我得到一个boost::system::error_code,其中包含"文件结束"。果然,插座不再打开了。但它是一个TCP套接字。在输入结束和关闭套接字时失败的重点是什么?更多输入将到达。我知道远程端没有关闭套接字,这是已知的正确实现该协议的生产代码。

异步读取的全部原因当然是不阻止等待响应。那么如何在前8个字节之后得到一个回调,而没有Asio关闭我的套接字?

现有问题相似但不同:要么他们不使用异步读取,要么他们不进行固定大小的读取,或者只有在第一次阅读后才会出现问题,或者他们遇到问题{ {1}}(我不会在EOF错误发生后按预期返回)

2 个答案:

答案 0 :(得分:3)

文件结束(boost::asio::error::eof)表示远程对等方关闭了连接。它并不表示该流没有可供读取的数据。 Boost.Asio streams documentation声明:

  

流的结尾可能导致readasync_readread_untilasync_read_until函数违反合同。例如。由于NEOF字节的读取可能会提前完成。

虽然可能由于未定义的行为而发生此错误。 Boost.Asio要求提供给async_read()input)的缓冲区必须保持有效,直到调用处理程序(callback)。

此外,如果套接字已在本地关闭,则操作错误将为boost::asio::error::operation_aborted


这是一个可用于演示此行为的基本应用程序:

#include <boost/array.hpp>
#include <boost/asio.hpp>

void on_read(
  const boost::system::error_code& error,
  std::size_t bytes_transferred)
{
  std::cout << "read " << bytes_transferred << " bytes with "
            << error.message() << std::endl;
}

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    std::cerr << "Usage: <port>\n";
    return 1;
  }

  // Create socket and connet to local port.
  namespace ip = boost::asio::ip;
  boost::asio::io_service io_service;
  ip::tcp::socket socket(io_service);
  socket.connect(ip::tcp::endpoint(
      ip::address::from_string("127.0.0.1"), std::atoi(argv[1])));

  // Start read operation.
  boost::array<char, 512> buffer;
  async_read(socket, 
             boost::asio::buffer(buffer),
             boost::asio::transfer_at_least(7),
             &on_read);

  // Run the service. 
  io_service.run();
}

以下演示了两次写入TCP连接。第一次写入足够小,客户端将在下一次写入之前读取所有流的数据。

$ nc -l 12345 &
[1] 11709
$ ./a.out 12345 &
[2] 11712
$ fg 1
nc -l 12345
helloenter
worldenter
read 12 bytes with Success

相同的程序,但连接被明确杀死:

$ nc -l 12345 &
[1] 11773
$ ./a.out 12345 &
[2] 11775
$ fg 1
nc -l 12345
helloenter
worldctrl-c
read 6 bytes with End of file

答案 1 :(得分:0)

原则上是协议实施中的竞争条件。如果未接受初始消息,则远程端关闭套接字。发送初始消息后,本地方确实检查了这一点。但是,在转移到异步操作之后,在本地返回异步写入之后以及异步读取之前完成套接字检查。

无法准确预测远程端何时关闭套接字,您无法等待。你唯一知道的是,如果你收到这8个字节,它就没有关闭套接字。因此,异步读取时的EOF结果是我必须处理的事情。

我怀疑现有的(同步)代码可能有类似的时序问题尚未浮出水面:(