async_read_until函数以及streambuf如何使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Bobsweetie/article/details/78491828

async_read_until

  • 在用读取串口的时候我使用了async_read_until函数,具体为
    boost::asio::async_read_until(sp, rec_buf, ‘#’, boost::bind(&BaseControl::HandleRead, this, _1, _2));
    本来以为这个函数会读到’#’就自动返回,但是实际的情况是,读到’#’之后还会读取更多的字符。后面通过仔细查看函数的说明发现了下面的语句:

    After a successful async_read_until operation, the streambuf may
    contain additional data beyond the delimiter. An application will typically
    leave that data in the streambuf for a subsequent async_read_until operation
    to examine

  • 就是说读到分隔符之后,还会读取更多的数据,但是在回调函数中的bytes_transferred数目是从读取的第一个不是’#’的字符开始一直到是’#’的数据为止,并不包括之后的数据。比如rec_buf中读到的数据是’123#456’,最后bytes_transferred的数值是4,而不是7。
    回调函数:

void BaseControl::HandleRead(boost::system::error_code ec, std::size_t bytes_transferred)
{
    //cout.write(rec_buf, bytes_transferred);
    bytes_read = bytes_transferred;

    printf("bytes_read: %ld\n", bytes_read);
}
  • 既然字符串中有多余的字符,怎样恰好的读到我所需要的字符串呢?rec_buf是boost::asio::streambuf类型,
    可以使用std::getline函数,这个函数可以刚好读到分割符’#’,具体用法参考:http://www.cplusplus.com/reference/string/string/getline/
    下面是代码示例:
  boost::asio::streambuf b;
  std::ostream os(&b);

  os << "H#e#l#l#o#W#o#r#l#d#!\n";
  std::string str;

  std::istream is(&b);
  while(b.size() !=0)
  {
    std::getline(is, str, '#');
    std::cout << str << std::endl;
  }

boost::asio::streambuf

  • boost::asio::streambuf和C++标准库中的流对象非常的相似,数据会写入到输出流,可以从输入流读取数据,比如可以用std::cout.put()从标准输出流输出数据,用std::cin.get()从标准输入流读取数据。当手动控制streambuf的时候,通常的过程是下面这样的:
    1.streambuf通过prepare()为输出序列准备空间;
    2.当数据被写入到streambuf的输出序列后,数据会被调拨走,调拨走的数据会从输出序列移动后并追加到到输入序列,
    这样就可以被读取到;
    3.数据通过data()函数从输入序列读取走;
    4.一旦数据从输入序列读取走后,就可以用consume函数从输入序列读取走。

附上下面这张图,理解更加深刻:
这里写图片描述
其中:
consume:从输入序列中移除字符
commit:将字符从输出序列移动到输入序列

  • 当使用Boost.Asio操作streambuf或者使用streambuf的流对象的时候,比如说std::ostream,潜在的输入输出序列会被合理的管理,就是说不用在代码里调用commit和consume等函数,已经自动的调用了;但是当buffer是提供给一个操作的时候,比如说传递prepare()给一个读操作,或者data()给一个写操作,那么代码里必须显式的处理commit()函数和sonsume()函数。下面是几个例子:

下面的例子是从streambuf直接写数据到socket:

// The input and output sequence are empty.
boost::asio::streambuf b;
std::ostream os(&b);

// prepare() and write to the output sequence, then commit the written
// data to the input sequence.  The output sequence is empty and
// input sequence contains "Hello, World!\n".
os << "Hello, World!\n";

// Read from the input sequence, writing to the socket.  The input and
// output sequences remain unchanged.
size_t n = sock.send(b.data());

// Remove 'n' bytes from the input sequence. If the send operation sent
// the entire buffer, then the input sequence would be empty.
b.consume(n);

下面的例子是从socket接收数据到streambuf,这个粒子假设”hello”已经被接受了,但是没有读取:

boost::asio::streambuf b;

// prepare() 512 bytes for the output sequence.  The input sequence
// is empty.
auto bufs = b.prepare(512);

// Read from the socket, writing into the output sequence.  The
// input sequence is empty and the output sequence contains "hello".
size_t n = sock.receive(bufs);

// Remove 'n' (5) bytes from output sequence appending them to the
// input sequence.  The input sequence contains "hello" and the
// output sequence has 507 bytes.
b.commit(n);

// The input and output sequence remain unchanged.
std::istream is(&b);
std::string s;

// Read from the input sequence and consume the read data.  The string
// 's' contains "hello".  The input sequence is empty, the output
// sequence remains unchanged.
is >> s;

下面的例子表明,全部使用流对象操作streambuf的时候,不需要用consume函数和commit()函数:

  boost::asio::streambuf b;
  std::ostream os(&b);

  os << "H#e#l#l#o#W#o#r#l#d#!\n";
  std::string str;

  std::istream is(&b);
  while(b.size() !=0)
  {
    std::getline(is, str, '#');
    std::cout << str << std::endl;
  }
  • 通过上面的例子可以看出,流对象可以自己调用commited和consuming操作来处理streambuf的输出和输入序列;
    但是当streambuf的缓冲自己被使用的的时候(比如通过data()或prepare()函数),那么必须用代码显式的调用
    commits和consumes。

参考:https://stackoverflow.com/questions/31960010/boost-asio-streambuf

猜你喜欢

转载自blog.csdn.net/Bobsweetie/article/details/78491828
今日推荐