boost::asio 数据拥有`ConstBufferSequence`

Posted

技术标签:

【中文标题】boost::asio 数据拥有`ConstBufferSequence`【英文标题】:boost::asio data owning `ConstBufferSequence` 【发布时间】:2020-06-26 16:55:29 【问题描述】:

最后几天,我阅读了很多关于 SO 的 asio 示例和其他问题,这些问题与传递给 asios 启动函数的缓冲区的生命周期管理有关。让我印象深刻的一个问题是,似乎没有“按值”解决方案,而是启动函数的调用者始终需要确保缓冲区在异步操作完成之前是有效的。这通常是通过创建指向数据的共享指针并将它们复制到处理程序中来实现的。

就我而言,我已经在使用 async_write 重载,其中传递了 ConstBufferSequence,其中包含例如下一条消息的大小,后跟下一条消息。即

auto message = std::make_shared<std::string>("hello");
auto size = std::make_shared<std::uint32_t>(message.size());
std::vector<asio::const_buffer> buffers = asio::buffer(size, sizeof(*size)), asio::buffer(*message);
asio::async_write(_socket, buffers, [message,size](...)...); // prolong lifetime by coping shared ptrs.

所以我正在考虑编写一个自定义的Message 类,该类满足ConstBufferSequence 的概念,但也拥有基本信息。我对代码进行了一些研究,发现在一个地方,缓冲区序列参数最初通过const&amp; 传递给asio::async_write,然后通过const&amp; 传递,直到它最终被复制到asio::detail::write_op 类。

所以这里是实际问题:

这种方法可以用于火灾而忘记呼叫吗?这将在上面转换成类似的内容: asio::async_write(_socket, Message("hello"),[](auto,auto));

constbuffer 序列参数的生命周期是否有任何问题 w.r.t.组合操作?

这是个好主意吗?这显然违背了处理缓冲区生命周期管理的“正常” asio 方式。

对您的想法感到好奇 - 欢迎批评! ;-)

问候,马丁

【问题讨论】:

【参考方案1】:

我以前见过shared_buffer(我认为它在 Asio 文档/示例中,稍后会搜索)。

显然还有

boost::asio::streambuf Beast 缓冲区模型(flat_buffer、multi_buffer、vector_buffer)

当然,它只是将问题从所有权问题转变为终身问题。

Boost Asio 的缓冲区概念是“仅引用”或“视图语义”的好处在于

您永远不会遇到按值传递这些引用的终生问题,并且 它们非常适合以正确的方式实现组合操作:无需对缓冲区组织进行假设

找到了 Asio 示例:https://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/example/buffers/reference_counted.cpp 这些天生活在

example/cpp03/buffers/reference_counted.cpp example/cpp11/buffers/reference_counted.cpp

解决问题

这种方法可以用于火灾而忘记呼叫吗?这会将上面的内容转换为: asio::async_write(_socket, Message("hello"),);

是的

constbuffer 序列参数的生命周期是否有任何问题 w.r.t.组合操作?

如果你管理得好就不会,反正这是一个要求

这是个好主意吗?这显然违背了处理缓冲区生命周期管理的“正常” asio 方式。

取决于你如何做到这一点。如果您最终批量复制缓冲区,成本会很高,并且也不适用于某些异步操作(这需要缓冲区具有引用稳定性,这意味着:不更改地址,换句话说,不要四处走动)。

虽然有了移动语义,许多缓冲区类型可能仍然能够提供此属性,但按值传递缓冲区的问题是它们可能会制作多个副本,这

两者都不能编译(因为它们不是右值) 或者(如果您使用移动的复制构造函数/赋值对其进行破解)在使用移动的副本时可能会导致 UB

【讨论】:

添加了 shared_const_buffer 示例并解决了各个问题的要点 我认为通过 boost beast 文档了解可用的DynamicBuffer 实现给了我剩下的:D - 我认为我的计划类似于野兽动态缓冲区实现,但我不会走那条路.我得出结论,它只是混淆了我的上下文中发生的事情。而且“一劳永逸”的方法真的只在发送(而不是接收)时有帮助,所以我想它不值得提出一个适当的自己的实现......谢谢你给我指出正确的方式:) @MartiNito 干杯。我同意你的评价。对于“fire and forgt”,我发现拥有std::list&lt;std::string&gt; 或类似名称通常非常方便,因为这也有助于在全双工场景中排队传出消息。 std::list 具有参考稳定性,非常完美。

以上是关于boost::asio 数据拥有`ConstBufferSequence`的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::asio 丢弃数据

传递给 boost::asio::async_read_some 的回调从未在 boost::asio::read_some 返回数据的使用中调用

Boost.Asio 的同步和并发数据结构模式

boost::asio::streambuf 通过 https 检索 xml 数据

为啥 boost::asio::read 缓冲区数据大小小于读取大小?

使用 boost::asio::async_wait_until 和 boost::asio::streambuf