【问题标题】:Writing sections of data to TCP socket with boost::asio::ip::tcp from streambuf使用来自 streambuf 的 boost::asio::ip::tcp 将部分数据写入 TCP 套接字
【发布时间】:2023-03-13 21:31:01
【问题描述】:

请注意那些不赞成投票的人:这个问题与 asio 的异步方面无关(尽管在这里异步解决方案可能有意义,这是我最后提出的一个问题)。这实际上只是将 streambuf 和 ostreams 与 asio tcp 套接字包装器一起使用。示例/教程不涵盖此特定方面(细分写入调用)。

我正在为插件环境编写一些(希望是简单的)代码,该插件环境需要将相当大的数据块(~2MB)发送到外部服务器以响应某些事件。数据需要相当迅速、完整地发送,但这种情况很少见,我并不太担心原始性能。我正在使用 Google 的协议缓冲区来序列化数据。

截至目前,我有以下几乎可以工作的代码:

#include <boost/asio.hpp>

// connect to the server:
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(server_address, server_port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);

// float_array consists of ~500,000 floats in a ProtoBuf message:
//
// message FloatArray {
//   repeated float data = 1 [packed=true];
// }

// send the serialized float_array to the server:
boost::asio::streambuf b;
std::ostream os(&b);
float_array.SerializeToOstream(&os);
boost::asio::write(socket, b);

// the TCP connection *must* now close to signal the server

问题在于我正在工作的环境(多线程)认为write() 操作花费了太长时间(它阻塞),并终止了线程。由于不允许我创建自己的线程,我需要将 write() 操作拆分为多个单独的写入。

我关心的是如何做到这一点。我知道我可以使用它来发送确切的字节数:

boost::asio::write(socket, b, boost::asio::transfer_exactly(65536));

但是要正确地做到这一点,我需要确切地知道 ostream 中还剩下多少字节。我注意到b.size() 相应减少,所以我可以使用它。然而,为了拆分我的写作,我需要在调用这个函数之间存储一些状态。

我有一个选择是在调用我的写入函数之间存储 streambuf b 和 ostream os,但我想知道是否有更好的方法来做到这一点。据我所知,部分序列化 ProtoBuf 输出是不可能的,所以我认为我只能对float_array.SerializeToOstream() 进行一次调用。那么问题是是否有适当的方法可以直接查询 ostream 以获取可用字节数,或者可能利用其他机制(boost::asio::buffer 可能?)。

我很高兴自己查看 boost::asio 文档,我只是在寻找有关如何继续的一些指导,因为有很多文档需要处理,我不确定哪些部分的谜题是相关的。

一个想法 - 是否可以使用 boost::asio 来创建某种单线程异步“发送器”,这些线程可以为我处理这种状态?例如,我可以调用某种非阻塞的write() 函数,然后有某种回调(或经常访问的函数)来检查是否完成,然后关闭 TCP 连接?

【问题讨论】:

  • 为什么这被否决了?某种建设性的反馈将是有用的,而不是沉默和懦弱的否决。不受欢迎。
  • 你的问题被否决了,因为在问这个问题之前你似乎没有努力学习 Asio basics。 Asio 的全部目的是提供一种简单而优雅的方式来执行asynchronous i/o。您不必一次阅读所有文档,但至少值得阅读tutorial
  • 其实并非如此。我已经浏览了一些教程(我承认不是全部,UDP 这次不相关)和一些例子,这对达到这一点很有用。但是我不希望使用 asio 的异步功能,实际上我只使用 asio 是因为 SO 上的其他答案建议将其用于“基本”套接字 IO。我只想使用套接字包装器,而不是我更习惯的 C 风格调用。也许 asio 不是正确的解决方案,这是我的问题的一部分。
  • 无论如何,事实证明我的主要问题是在服务器端,过早地终止了连接。旧式套接字调用显示“管道损坏”,这帮助我快速诊断出这个问题。这也是“完整” write() 调用引发奇怪异常的原因。所以在修复服务器之后,完整的写入现在可以正常工作了。同样,我不想使用 asio 的异步功能,只是使用套接字包装器。如果不清楚,我很抱歉。

标签: c++ tcp boost-asio protocol-buffers iostream


【解决方案1】:

虽然我无法找到一种方法来查询 ostream 来直接确定流中“等待”的数据量,但我能够完全避免 ostream 并将数据序列化为 char* 数组。然后可以使用 boost::asio::write() 函数以与旧式 C 套接字方法类似的方式发送它:

...
tcp::socket socket(io_service);
char * buffer = new char[size];  // or a smart-ptr
float_array.SerializeToArray(static_cast<void*>(buffer, size));
void * p = static_cast<void*>(buffer);
int bytes_sent = boost::asio::write(socket, boost::asio::buffer(p, bytes_to_send);

或者,如果首选使用boost::asio::streambufstd::ostream,那么在使用ostream 写入一些数据之后,似乎可以查询streambuf(使用.size()):

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

// send a chunk of a particular size
int bytes_to_send = std::min(chunk_size, b.size());
cout << b.size() << endl;  // shows amount of remaining data
boost::asio::write(socket, b, boost::asio::transfer_exactly(bytes_to_send));
cout << b.size() << endl;  // shows a reduction in amount of remaining data

因此,如果多次调用(对于每个块),则需要将 ostream、streambuf 和 io_service 保持在范围内。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2014-01-24
    相关资源
    最近更新 更多