使用 async_read_until 时 boost::asio 是否截断输出?
这个把我难住了!
如果我使用 async_read_until
调用,打印超过 4k 字节的内容似乎会出现问题?
我有一个小函数可以打印 100 行(刚刚超过 4k)。
除非我注册 async_read_until 回调,否则在每种组合中都可以正常工作。那时我的输出被截断为大约 4k。请注意,并非总是如此,有时会更少,有时会打印整个内容,这似乎与机器上的负载有关,几乎就像发生了一些超时?一些 asio 线程的东西?无论如何,如果我注释掉 async_read_until
调用,无论我调用 printLines
多少次,它每次都能正常工作。我什至可以使用 ioService
post
功能,它工作正常......
发生了什么事?顺便说一句,我使用的是linux和amd64机器gcc4.4。 (Redhat)
使用 linux 'strace' 我得到了更多线索:
似乎在使用 fcntl 调用调用 async_read_until、asio 之后,导致我的输出文件描述符改变行为?
一段时间后它退出打印输出:
select(4, [0 3], [], [], {300, 0}) = 1(在 [0] 中,左 {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, "这是一个长句子的测试"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "这是一个长句子的测试"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "这是一个长句子的测试"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "这是一个长句子的测试"..., 93) = -1 EAGAIN (资源暂时不可用)
write(2, "这是一个长句子的测试"..., 93) = -1 EAGAIN (资源暂时不可用)
...剩下的直到达到 100。
所以你可以看到选择循环等待我输入返回键。然后我们称我为空 读取处理程序并退出 ioService。此时我调用 printLines 函数并尝试打印 100 行,但在打印 40 行后它退出了。
此 EAGAIN 的某些原因导致输出停止写入。
再次,如果我不调用 async_read_until 我的 printf 不会损坏。
我想我知道发生了什么,似乎 Asio 正在将我的标准输出文件描述符转换为异步非阻塞模式我请求标准输入文件描述符上的异步读取模式。这就是为什么在一些输出之后我在写入时收到 EAGAIN 错误。当然 printf 会忽略这些,因此我的输出被截断。不知道这是 Asio 中的错误还是 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;
}
This one has me stumped!
There seems to be a problem printing out anything over 4k bytes if I use the async_read_until
call?
I have a little function that prints out 100 lines (just over 4k).
Works fine in every combination except if I register a async_read_until
callback. At that point my output is truncates to about 4k. Note not always though, sometimes less and sometimes the whole thing is printed it seems to be related to the load on the machine, almost like there is some timeout going on? Some asio threading thing? Anyway if I comment out the async_read_until
call it works fine every time no matter how many time I call printLines
. I can even use the ioService
post
function it works fine...
What is going on? BTW, I'm using linux and amd64 machine gcc4.4. (Redhat)
Using linux 'strace' I got some more clues:
It seems like after calling async_read_until, asio using fcntl calls, causes my output file descriptor to change behaviour?
it quits printing output after awhile:
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, "This is a test of a long sentenc"..., 93) = 93
write(2, "This is a test of a long sentenc"..., 93) = 93
... about 40 times
write(2, "This is a test of a long sentenc"..., 93) = 53
write(2, "This is a test of a long sentenc"..., 93) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "This is a test of a long sentenc"..., 93) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "This is a test of a long sentenc"..., 93) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "This is a test of a long sentenc"..., 93) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "This is a test of a long sentenc"..., 93) = -1 EAGAIN (Resource temporarily unavailable)
... for the rest until 100 is reached.
So you can see the select loop waiting for my input return key. Then we call my empty
read handler and drop out of the ioService. At this time I call my printLines function and try to print 100 lines but it quits after printing 40 some.
Something with this EAGAIN is causing the output to stop writting.
Again if I don't call async_read_until my printf does not get corrupted.
I think I know what is going on, seems like Asio is turning my stdout file desciptor into async non-blocking mode when I request async read mode on the stdin file descriptor. That's why after some output I get EAGAIN errors on writes. Of course printf ignores those so my output is truncated. Don't know if that is a bug in Asio or just a side effect on linux?
Here is my simple program to duplicate the problem:
#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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果我在
printLines
之前插入对ioctl
的以下调用,则该行为是预期的
虽然我不清楚为什么会发生这种情况,但这可能是
ioctl 的副作用
,这主要是特定于内核/设备的。在浏览各个手册页时,我没有看到任何描述这种行为的内容。您可以尝试从在printLines
中使用fprintf
切换到使用posix::stream_descriptor
和STDERR_FILENO
。编辑:看起来 Boost.Asio 发生了一些最近的更改解决这种行为。具体来说
我看起来很有趣。我在 Mac 上使用 Boost 1.45 来运行您的重现器,这些更改将出现在即将发布的 Boost 1.47 中。我将尝试获取 Chris 最新的 asio 开发版本,看看行为是否发生了变化。
If I insert the following call to
ioctl
prior toprintLines
the behavior is expected
Though it's not clear to me why this happens, it could be a side effect of
ioctl
, which is largely kernel/device specific. I did not see anything describing this behavior when trolling around the various man pages. You might try switching from usingfprintf
inprintLines
to using aposix::stream_descriptor
withSTDERR_FILENO
instead.Edit: it looks like there have been some recent changes to Boost.Asio that may address this behavior. Specifically
look interesting to me. I used Boost 1.45 on my mac to run your reproducer, and these changes will be in the upcoming Boost 1.47. I'll try to grab Chris's latest asio development release and see if the behavior has changed.