近段有相当一部分时间在熟悉和练习 ASIO

练习过程中发现 ASIO 中如何使用&管理 buffer 是新手大概率会遇到的问题。

结合最近几个 practice demo,稍微简单总结了一下使用经验:

0x00

const_buffermutable_buffer 是两个 fundamental buffer classes。二者的区别在语义上表达的很明显了。

实现上二者提供的接口非常一致,除了一个面向 const void*,另一个面向 void*。这点可以从 ctor 和 data() 中看出。

另外,为了和 C++ 现有的 const cast semantics 保持一致,一个 mutable_buffer 对象可以 implicitly converted to const_buffer

IO 操作上,const_buffer 通常对应一个 write operation,而 mutable_buffer 通常对应一个 read operation。

0x01

asio::buffer() 可以根据用户提供的 buffer 创建一个 const_buffermutable_buffer

并且因为上面提到的 buffer-constness semantics,哪怕此时创建出来的是 mutable_buffer,也能自动转换到目标需要的 const_buffer

所以大部分场合下,只需要使用 asio::buffer() 构建 IO buffer。

0x02

上面说的 IO buffer 非常像 std::string_view,它们只 refer 某一块现有的内存,不会获得内存的 ownership。

因此,使用者必须保证用作 buffer 的内存的生命周期在整个 IO 操作期间都有效。

这是刚使用 ASIO 时很容易犯的一个错误。

对于有 Windows IOCP 编程经验的人来说,这点很容易理解,因为异步 IO 必须事先提供好内存,而 ASIO 采纳的刚好也是 proactor model。

0x03

对于 asio::async_read_until(),配套的 buffer 是 dynamic buffer

因为这个函数需要读到某个 delim 或者 pred 满足才会返回,因此 buffer 大小无法实现确定。

dynamic buffer 的要求之一就是支持 memory storage can be automatically resized as required

另,满足 dynamic buffer 的前提条件是:需要是一个 const buffer 或者 mutable buffer。

使用上,std::stringstd::vector<char> 可以直接作为底层存储使用。

asio::dyanmic_buffer() 可以直接创建 dynamic buffer class。

0x04

如果接受了上面 dynamic buffer 的设定,那么一定要注意,对于普通的 const buffer 或者 mutable buffer,如果使用 asio::buffer() 构造时不显式指定大小,那么内部会直接使用底层存储的 size() 作为大小。

如果底层存储为 empty,那么会导致每次 IO 操作提供的 buffer 为空,会导致 IO 操作立即返回,且 std::error_code 没有任何错误。

这里和使用 dyanmic buffer 不同。

我在实现基于 ASIO 的 socks4a proxy 时就被这个细节坑了一下。