使用Boost::asio::async_read进行第一次读取时的EOF

EOF on first read with Boost::asio::async_read

本文关键字:读取 第一次 EOF read asio Boost async 使用      更新时间:2023-10-16

我正在使用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对我的套接字?

现有的问题是相似的,但不同:要么他们不使用异步读取,要么他们不做固定大小的读取,或者他们只有在第一次读取后才有问题,或者他们有io_service::run问题(我不,它在EOF错误发生后按预期返回)

End of file (boost::asio::error::eof)表示对端已关闭连接。它并不表示流没有更多的数据可读。的提振。Asio streams文档说明:

流的结束可能导致readasync_readread_untilasync_read_until函数违反它们的契约。例如,N字节的读取可能会因为EOF而提前完成。

虽然这个错误可能是由于未定义的行为而发生的。提振。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美元/。输出12345 &[2] 11712$ fg 1Nc -l 12345你好输入世界输入读取12字节成功之前

相同的程序,但连接被显式终止:

<>之前$ nc -l 12345 &[1] 11773美元/。输出12345 &[2] 11775$ fg 1Nc -l 12345你好输入世界 ctrl - c 读取6字节与文件结束

原来是协议实现中的竞争条件。如果未接受初始消息,则远程端关闭套接字。在发送初始消息后,当地确实对此进行了检查。然而,在转到异步操作之后,套接字检查是在本地返回异步写操作之后和异步读操作之前完成的。

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

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