【问题标题】:Does boost::asio truncate output when using async_read_until?使用 async_read_until 时 boost::asio 会截断输出吗?
【发布时间】:2011-04-10 11:05:56
【问题描述】:

这个让我难过

如果我使用async_read_until 调用,打印超过 4k 字节的内容似乎有问题?

我有一个小函数可以打印出 100 行(刚好超过 4k)。

除非我注册了async_read_until 回调,否则在每种组合中都可以正常工作。那时我的输出被截断到大约 4k。请注意,并非总是如此,有时打印较少,有时打印整个内容似乎与机器上的负载有关,几乎就像发生了一些超时?一些asio线程的东西?无论如何,如果我注释掉async_read_until 调用,无论我调用多少次printLines,它每次都能正常工作。我什至可以使用 ioService post 函数,它工作正常...

发生了什么事?顺便说一句,我使用的是 linux 和 amd64 机器 gcc4.4。 (红帽)


使用 linux 'strace' 我得到了更多线索:

似乎在调用 async_read_until 之后,使用 fcntl 调用的 asio 会导致我的输出文件描述符改变行为?
一段时间后它会退出打印输出:

select(4, [0 3], [], [], {300, 0}) = 1 (in [0], left {298, 830000})
readv(0, [{"\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0\0"..., 512}], 1) = 1
write(2, "这是一个长句子的测试"..., 93) = 93
write(2, "这是一个长句子的测试"..., 93) = 93
... 大约 40 次
write(2, "这是一个长句子的测试"..., 93) = 53
write(2, "This is a test of a long sentence"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "This is a test of a long sentence"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "This is a test of a long sentence"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "This is a test of a long sentence"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "This is a test of a long sentence"..., 93) = -1 EAGAIN (资源暂时不可用)
...其余的,直到达到 100。

所以你可以看到选择循环等待我的输入返回键。然后我们称我为空 读取处理程序并退出 ioService。此时我调用了我的 printLines 函数并尝试打印 100 行,但在打印 40 行后退出。

这个 EAGAIN 导致输出停止写入。
再次,如果我不调用 async_read_直到我的 printf 没有损坏。


我想我知道发生了什么,当我在标准输入文件描述符上请求异步读取模式时,似乎 Asio 正在将我的标准输出文件描述符转换为异步非阻塞模式。这就是为什么在一些输出之后我在写入时收到 EAGAIN 错误。当然 printf 会忽略那些,所以我的输出被截断了。不知道这是 Asio 的 bug 还是 linux 的副作用?


这是我复制问题的简单程序:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace boost::asio;
using namespace std;

io_service ioService;
boost::asio::streambuf inStream;
posix::stream_descriptor input(ioService, STDIN_FILENO);

void printLines()
{
    for (int i = 0; i < 100; i++) {
        fprintf(stderr, "This is a test of a long sentence, there will be %d more sentences after this on is printed.\n", i);
    }
    fflush(stderr);
}

void readHandler(const boost::system::error_code& error)
{ // Don't care about read!! }

int main()
{
    boost::system::error_code ec;
    //printLines(); << this works if uncommented
    //ioService.post(printLines); << this works if uncommented

    boost::asio::async_read_until(input, inStream, "\n",
    bind(readHandler, placeholders::error)); // causes truncated output

    cout << "Hit Return to continue..." << endl;
    ioService.run_one( ec );
    assert(!ec);
    printLines(); // partial output if async_read_until is called?
    return 0;
}

【问题讨论】:

  • 你关于非阻塞标准错误的理论是合理的。您是否在 strace 输出中看到对描述符 2 的 fcntl 或 ioctl 调用?我在 RHEL 6 系统上运行了您的复制器,我没有看到 EAGAIN,但我确实看到了大约 60 行之后的截断输出。我确实注意到将 fprintf 更改为 std::cerr 时,问题消失了。

标签: c++ boost boost-asio


【解决方案1】:

如果我在printLines 之前插入以下对ioctl 的调用

int opt = 0;
ioctl( STDIN_FILENO, FIONBIO, &opt );
printLines();

行为是预期的

samm@macmini ~> ./a.out
Hit Return to continue...

This is a test of a long sentence, there will be 0 more sentences after this on is printed.
...
This is a test of a long sentence, there will be 96 more sentences after this on is printed.
This is a test of a long sentence, there will be 97 more sentences after this on is printed.
This is a test of a long sentence, there will be 98 more sentences after this on is printed.
This is a test of a long sentence, there will be 99 more sentences after this on is printed.
samm@macmini ~>

虽然我不清楚为什么会发生这种情况,但这可能是ioctl 的副作用,这主要是内核/设备特定的。在浏览各种手册页时,我没有看到任何描述此行为的内容。您可以尝试从在 printLines 中使用 fprintf 切换到使用带有 STDERR_FILENOposix::stream_descriptor


编辑:似乎 Boost.Asio 有一些 recent changes 可以解决此问题。具体

  • 添加了新的 non_blocking() 函数 管理非阻塞行为 套接字或描述符。这 io_control() 命令命名 non_blocking_io 现在已弃用 支持这些新功能。

  • 添加了新的 native_non_blocking() 用于管理的功能 底层的非阻塞模式 套接字或描述符。这些功能 旨在让 任意封装 非阻塞系统调用为 异步操作,在某种程度上 对用户是透明的 套接字对象。函数没有 对行为的影响 套接字的同步操作 或描述符。

对我来说看起来很有趣。我在我的 Mac 上使用 Boost 1.45 来运行您的复制器,这些更改将在即将推出的 Boost 1.47 中。我将尝试获取 Chris 最新的 asio 开发版本,看看行为是否发生了变化。

【讨论】:

  • 实际上我使用 stderr 只是为了强制刷新。我真的对使用标准的旧 std::cout 很感兴趣。实际上我希望只在读取回调处理程序中使用 std::cout ,但这就是一切的开始。当我尝试在读取处理程序中打印回读取的内容时,它会截断我的输出。我需要一些方法来进行异步读取,但要保持标准输出正常(阻塞)。 Chris 使用管道在 Asio 中做很多他的异步工作,我想知道这是否会影响我的标准输出?
  • 还有几个问题,为什么要使用 ioctrl 设置标准输入,为什么不使用标准输出并让标准输入保持非阻塞状态?另外,为什么不使用带有 O_NONBLOCK 的 fctrl 而不是 ioctrl?
  • @Oliver 它反转了 async_read_until 启用的非阻塞设置。正如我的回答所说,我怀疑这会产生连锁反应,并以某种方式导致与您的 tty 关联的其他描述符的相同行为。不过,我找不到太多确认或否认这种行为的文件。我不确定为什么 Boost.Asio 在这里使用 ioctl 而不是 fcntl,我相信 fcntl 更便携。
  • @Oliver 查看我最近的编辑,我发现了一张可能相关的票。
  • Hi Sam, thanks for the info... Curious, why can't I just get a read callback from asio when the select loop has something in stdin and then do a BLOCKING read of one line?似乎这就是我们在编写自己的选择循环时常用的方式?选择循环使它成为非阻塞的,它告诉你什么时候有东西要读,这样你就不会阻塞等待。为什么在 Asio 中必须非阻塞读取?
猜你喜欢
  • 1970-01-01
  • 2011-03-04
  • 2020-08-05
  • 1970-01-01
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多