为什么没有 "cursor" 调用者使用 ConstBufferSequence 缓冲区 async_send_some?
Why is there not a "cursor" for caller to async_send_some with ConstBufferSequence buffers?
在 boost asio 中,async_write_some
程序员可以以任何方式实现自己的类似于 async_write
的细粒度逻辑,等等:
class koebenhavn : public std::enable_shared_from_this<koebenhavn>
{
private:
boost::asio::ip::tcp::socket socket;
public:
...
void writing(std::vector<const_buffer> & buffers, boost::asio::io_context::strand & strand){
/* io worker may call back at arbitrary amount of transferred bytes */
socket->async_write_some(buffers,
boost::asio::bind_executor(strand, [self=shared_from_this(), &buffers, &strand](boost::system::error_code error, size_t transferred){
/* lambda instead of std::bind for specification if any possible overload */
self->wrote(buffers, strand, error);
}));
}
void wrote(std::vector<const_buffer> & buffers,
boost::asio::io_context::strand & strand,
boost::system::error_code error, size_t transferred){
/* we need to advance buffers to send the rest bytes in reference */
if(!error){
/* transfer occurred */
if(transferred){
/* ----------------> here we need to construct new "cursor" of buffers based on the previous */
std::vector<const_buffer> cursor = advance(buffers, transferred);
/* <---------------- repeat! */
writing(cursor, strand);
}
}
}
...
};
我想要的是一个简单的函数,用于为下一次重复调用推进缓冲区序列。现在我怀疑在 asio 中没有任何 present wheel,是吗?
我有充分的理由不使用 async_write。回复请不填
你所期望的存在。这是 DynamicBuffer 的概念。
read|wrte_some
成员函数是低级的,不采用动态缓冲区(它采用一系列 MutableBuffer)。事实上,我认为在 99% 的情况下使用这些函数并不是你想要的。例如。见 this remark:
Remarks
The read operation may not read all of the requested number of bytes. Consider using the async_read function if you need to ensure that the requested amount of data is read before the asynchronous operation completes
人们通常对 IO 数据包传递有错误的假设。只需将 async_read
函数与您的动态缓冲区一起使用:
boost::asio::streambuf
boost::asio::dynamic_buffer
带有 std::string
变量等
自己动手
当然,您 可以 将动态缓冲区(即带有 read/write “游标”)与任何低级接口(期望直接缓冲区)一起使用。 但是 那么你必须使用 prepare()
/consume()
/commit()
接口来管理游标(以及可能发生在底层存储中的可选分配) ).
您可以将其与 iostreams
进行比较:您可以使用底层的 streambuf 接口代替 int x; std::cin >> x;
,但它只会增加工作量并且容易出错。
当然,有时您想要那个低级接口(例如,当您希望能够更详细地了解什么数据包在什么时间到达时),但我认为那是例外遵守规则。
奖金:演示代码
解决问题代码的最简单方法是使用组合写入操作:
void writing(std::vector<const_buffer> const& buffers, strand& strand)
{
/* io worker may call back at arbitrary amount of transferred bytes */
boost::asio::async_write(
socket, buffers,
bind_executor(strand, [self = shared_from_this(), strand]
(error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
}));
}
这里的“游标”是由图书馆完成的。没有问题。
备注:
你可能更能接受buffer类型,接受DynamicBuffer(v1/v2)和ConstBufferSequence的任意模型:
template <typename Buffers>
void writing(Buffers&& buffers, strand& strand)
{
/* io worker may call back at arbitrary amount of transferred bytes */
boost::asio::async_write(
socket, std::forward<Buffers>(buffers),
bind_executor(strand, [self = shared_from_this(), strand]
(error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
}));
}
您可以将执行器与 IO 对象相关联,例如像这样:
koebenhavn(boost::asio::io_context& ctx)
: socket_(make_strand(ctx))
{ }
现在您可以使用 (boost::asio::associated_executor(socket_)
或 socket_.get_executor()
或 当您在该套接字上启动任何异步操作时让库默认执行此操作。
template <typename Buffers>
void writing(Buffers&& buffers)
{
/* io worker may call back at arbitrary amount of transferred bytes */
async_write(
socket, std::forward<Buffers>(buffers),
[self = shared_from_this()](error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
});
}
注意到事情变得简单了吗?是时候让它复杂化了。您的代码默认假设 writing(...)
将被安全调用,即从链中调用。由于我们不确定,请考虑发布或发送到 strand:
dispatch( //
socket_.get_executor(),
[this, self, b = std::forward_as_tuple(buffers)]() mutable {
async_write( //
socket_, std::get<0>(b),
[self](error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
});
});
使用 dispatch
的好处是图书馆可以 运行 如果它检测到您已经在路上,则可以立即执行任务。
现场演示
#include <boost/asio.hpp>
#include <iostream>
class koebenhavn : public std::enable_shared_from_this<koebenhavn> {
private:
boost::asio::ip::tcp::socket socket_;
using const_buffer = boost::asio::const_buffer;
using error_code = boost::system::error_code;
using strand = boost::asio::io_context::strand;
public:
koebenhavn(boost::asio::io_context& ctx)
: socket_(make_strand(ctx))
{ }
// io worker may call back at arbitrary amount of transferred bytes
template <typename Buffers> void writing(Buffers&& buffers)
{
auto self = shared_from_this();
dispatch( //
socket_.get_executor(),
[this, self, b = std::forward_as_tuple(buffers)]() mutable {
async_write( //
socket_, std::get<0>(b),
[self](error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
});
});
}
};
int main() {
boost::asio::io_context io;
auto k = std::make_shared<koebenhavn>(io);
boost::asio::streambuf sb;
k->writing(sb);
std::string s = "hello world\n";
k->writing(boost::asio::buffer(s));
std::array<float, 7> ff{};
std::vector<unsigned char> bb{{0, 1, 2, 3, 4}};
k->writing(std::vector{
boost::asio::buffer(ff),
boost::asio::buffer(bb),
});
}
在 boost asio 中,async_write_some
程序员可以以任何方式实现自己的类似于 async_write
的细粒度逻辑,等等:
class koebenhavn : public std::enable_shared_from_this<koebenhavn>
{
private:
boost::asio::ip::tcp::socket socket;
public:
...
void writing(std::vector<const_buffer> & buffers, boost::asio::io_context::strand & strand){
/* io worker may call back at arbitrary amount of transferred bytes */
socket->async_write_some(buffers,
boost::asio::bind_executor(strand, [self=shared_from_this(), &buffers, &strand](boost::system::error_code error, size_t transferred){
/* lambda instead of std::bind for specification if any possible overload */
self->wrote(buffers, strand, error);
}));
}
void wrote(std::vector<const_buffer> & buffers,
boost::asio::io_context::strand & strand,
boost::system::error_code error, size_t transferred){
/* we need to advance buffers to send the rest bytes in reference */
if(!error){
/* transfer occurred */
if(transferred){
/* ----------------> here we need to construct new "cursor" of buffers based on the previous */
std::vector<const_buffer> cursor = advance(buffers, transferred);
/* <---------------- repeat! */
writing(cursor, strand);
}
}
}
...
};
我想要的是一个简单的函数,用于为下一次重复调用推进缓冲区序列。现在我怀疑在 asio 中没有任何 present wheel,是吗?
我有充分的理由不使用 async_write。回复请不填
你所期望的存在。这是 DynamicBuffer 的概念。
read|wrte_some
成员函数是低级的,不采用动态缓冲区(它采用一系列 MutableBuffer)。事实上,我认为在 99% 的情况下使用这些函数并不是你想要的。例如。见 this remark:
Remarks
The read operation may not read all of the requested number of bytes. Consider using the async_read function if you need to ensure that the requested amount of data is read before the asynchronous operation completes
人们通常对 IO 数据包传递有错误的假设。只需将 async_read
函数与您的动态缓冲区一起使用:
boost::asio::streambuf
boost::asio::dynamic_buffer
带有std::string
变量等
自己动手
当然,您 可以 将动态缓冲区(即带有 read/write “游标”)与任何低级接口(期望直接缓冲区)一起使用。 但是 那么你必须使用 prepare()
/consume()
/commit()
接口来管理游标(以及可能发生在底层存储中的可选分配) ).
您可以将其与 iostreams
进行比较:您可以使用底层的 streambuf 接口代替 int x; std::cin >> x;
,但它只会增加工作量并且容易出错。
当然,有时您想要那个低级接口(例如,当您希望能够更详细地了解什么数据包在什么时间到达时),但我认为那是例外遵守规则。
奖金:演示代码
解决问题代码的最简单方法是使用组合写入操作:
void writing(std::vector<const_buffer> const& buffers, strand& strand)
{
/* io worker may call back at arbitrary amount of transferred bytes */
boost::asio::async_write(
socket, buffers,
bind_executor(strand, [self = shared_from_this(), strand]
(error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
}));
}
这里的“游标”是由图书馆完成的。没有问题。
备注:
你可能更能接受buffer类型,接受DynamicBuffer(v1/v2)和ConstBufferSequence的任意模型:
template <typename Buffers> void writing(Buffers&& buffers, strand& strand) { /* io worker may call back at arbitrary amount of transferred bytes */ boost::asio::async_write( socket, std::forward<Buffers>(buffers), bind_executor(strand, [self = shared_from_this(), strand] (error_code ec, size_t transferred) { std::cout << "Transferred " << transferred << " bytes " << "(" << ec.message() << ")" << std::endl; })); }
您可以将执行器与 IO 对象相关联,例如像这样:
koebenhavn(boost::asio::io_context& ctx) : socket_(make_strand(ctx)) { }
现在您可以使用
(boost::asio::associated_executor(socket_)
或socket_.get_executor()
或 当您在该套接字上启动任何异步操作时让库默认执行此操作。template <typename Buffers> void writing(Buffers&& buffers) { /* io worker may call back at arbitrary amount of transferred bytes */ async_write( socket, std::forward<Buffers>(buffers), [self = shared_from_this()](error_code ec, size_t transferred) { std::cout << "Transferred " << transferred << " bytes " << "(" << ec.message() << ")" << std::endl; }); }
注意到事情变得简单了吗?是时候让它复杂化了。您的代码默认假设
writing(...)
将被安全调用,即从链中调用。由于我们不确定,请考虑发布或发送到 strand:dispatch( // socket_.get_executor(), [this, self, b = std::forward_as_tuple(buffers)]() mutable { async_write( // socket_, std::get<0>(b), [self](error_code ec, size_t transferred) { std::cout << "Transferred " << transferred << " bytes " << "(" << ec.message() << ")" << std::endl; }); });
使用
dispatch
的好处是图书馆可以 运行 如果它检测到您已经在路上,则可以立即执行任务。
现场演示
#include <boost/asio.hpp>
#include <iostream>
class koebenhavn : public std::enable_shared_from_this<koebenhavn> {
private:
boost::asio::ip::tcp::socket socket_;
using const_buffer = boost::asio::const_buffer;
using error_code = boost::system::error_code;
using strand = boost::asio::io_context::strand;
public:
koebenhavn(boost::asio::io_context& ctx)
: socket_(make_strand(ctx))
{ }
// io worker may call back at arbitrary amount of transferred bytes
template <typename Buffers> void writing(Buffers&& buffers)
{
auto self = shared_from_this();
dispatch( //
socket_.get_executor(),
[this, self, b = std::forward_as_tuple(buffers)]() mutable {
async_write( //
socket_, std::get<0>(b),
[self](error_code ec, size_t transferred) {
std::cout << "Transferred " << transferred << " bytes "
<< "(" << ec.message() << ")" << std::endl;
});
});
}
};
int main() {
boost::asio::io_context io;
auto k = std::make_shared<koebenhavn>(io);
boost::asio::streambuf sb;
k->writing(sb);
std::string s = "hello world\n";
k->writing(boost::asio::buffer(s));
std::array<float, 7> ff{};
std::vector<unsigned char> bb{{0, 1, 2, 3, 4}};
k->writing(std::vector{
boost::asio::buffer(ff),
boost::asio::buffer(bb),
});
}