【问题标题】:Reading from boost asio streambuf keeps bytes in buffer从 boost asio streambuf 读取将字节保留在缓冲区中
【发布时间】:2018-04-03 18:43:49
【问题描述】:

我在从 boost asio 的流中读取时遇到问题。 async_read_until 的第一次调用给了我 460 个字节的传输(我用 wireshark 检查过)。之后,我使用使用 streambuf 指针初始化的 istream 来使用带有 istreambuf_iterator 的 std::copy_n。这工作正常,std::copy_n 的目标将请求保持到分隔符序列。

奇怪的事情发生在下一次调用 async_read_until 之后。似乎没有从 streambuf 中读取最后一个字符,因此下一个处理程序调用给了我比请求的实际大小多一个字节。

将 istream 与 asio 的 streambuf 一起使用有什么限制吗?

【问题讨论】:

  • 您是否正确使用了commit()consume() 函数?你能展示我可以复制粘贴到我的 IDE 中检查的最少、完整的代码吗?
  • 我不使用任何提交或消费功能。我只是将流缓冲区用于 std::istream 并使用 istreambuf_iterators。经过一番试验,我发现了问题: std::copy_n 正在用迭代器做奇怪的事情。使用 while 循环并调用 ++ 运算符可以正常工作。不使用 commit 或 consume 并仅从 streambuf 制作 ostream 或 istream 有什么问题吗?
  • 如果您在 istream 或 ostream 中使用流缓冲区,则提交/使用机制已为您完成。
  • 好的,现在一切都很好。我最终没有使用 std::copy_n 因为它似乎省略了消耗最后一个字符,所以它仍然保留在缓冲区中。这会干扰下一次读取操作。不知道这是否是 std::copy_n 的意图。
  • @Gustavo copy_n 打算复制n 个字符。由于源是一个输入迭代器,它应该被期望增加(最多)n 次。 en.cppreference.com/w/cpp/algorithm/copy_n

标签: c++ boost boost-asio


【解决方案1】:

除了 cmets,这里还有一个小演示程序,展示了与 asio::streambuf 交互的两种方式。

一种方法是在 i/o 流中包装 streambuf,另一种方法是使用 prepare/commit 和 data/consume 直接访问。

#include <boost/asio.hpp>
#include <iostream>
#include <string>
#include <algorithm>
#include <memory>

namespace asio = boost::asio;

void direct_insert(asio::streambuf& sb, std::string const& data)
{
    auto size = data.size();
    auto buffer = sb.prepare(size);
    std::copy(begin(data), end(data), asio::buffer_cast<char*>(buffer));
    sb.commit(size);
}

void stream_insert(asio::streambuf& sb, std::string const& data)
{
    std::ostream strm(std::addressof(sb));
    strm << data;
}

std::string extract_istream(asio::streambuf& sb)
{
    std::istream is(std::addressof(sb));
    std::string line;
    std::getline(is, line);
    return line;
}

std::string extract_direct(asio::streambuf& sb)
{
    auto buffer = sb.data();
    auto first = asio::buffer_cast<const char*>(buffer);
    auto bufsiz = asio::buffer_size(buffer);
    auto last = first + bufsiz;

    auto nlpos = std::find(first, last, '\n');

    auto result = std::string(first, nlpos);

    auto to_consume = std::min(std::size_t(std::distance(first, nlpos) + 1), bufsiz);
    sb.consume(to_consume);
    return result;
}

int main()
{
    asio::streambuf buf;
    direct_insert(buf, "The cat sat on the mat\n");
    stream_insert(buf, "The cat sat on the mat\n");

    auto s1 = extract_direct(buf);
    auto s2 = extract_istream(buf);

    std::cout << s1 << "\n" << s2 << "\n";
}

【讨论】:

  • 好! +1但是您知道如何在精确位置写入缓冲区吗?我想清除它,即用零填充它。但即使在调用 direct_insert 之前调用 pubseekpos(-n),修改后的字节仍然在最后一个位置之后。好像 direct_insert 不关心当前位置?还是我错了?..
  • @yO_ 你需要澄清你的用例。如果您觉得需要在读取缓冲区之前对其进行修改,这可能意味着对异步 io 的工作方式有误解。
  • 谢谢。我在 asio 中遇到了一个奇怪的错误,在第二个 async_read_until 之后永远不会调用处理程序。 (Wireshark 和 Visual Studio 调试器显示消息 已接收 并覆盖了缓冲区的开头。)因此,即使它不是声称的 asio 行为,我也会在第二次读取之前清除缓冲区。像第一次一样设置它 - 好的。如果您能在某处指出误解,非常感兴趣。
  • @yO_ 如果您能够将示例程序放在 github 或 gitlab 上并与我分享,我可以帮助您调试它。我的电子邮件:hodges.r@gmail.com 确保应用程序简短!并且只显示问题。
  • @yO_ 拉取请求等待
猜你喜欢
  • 2016-09-19
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-26
  • 2017-03-26
  • 1970-01-01
相关资源
最近更新 更多