注意:我在这篇文章中交替使用“客户端”和“孩子”这两个词来指代从“服务器”启动的进程。
我正在使用 boost::process::async_pipe 编写使用 boost::process::child 启动的进程的 STDIN。假设我的服务器程序看起来像这样:
(这不是一个有效的服务器演示)
服务器.cpp
int main()
{
using namespace std::chrono_literals;
boost::process::async_pipe writePipe;
boost::process::child child { "client", boost::process::std_in < _writePipe };
std::vector<char> buffer;
buffer.resize(1024u * 1024u);
while (working)
{
auto length = 0u;
/*
do a bunch of work that takes a long time
and also determines `length`, in this case I'm
adding a sleep to simulate the time between
calls to `async_write()`
*/
std::this_thread::sleep_for(5s);
boost::asio::async_write(writePipe,
boost::asio::buffer(buffer.data(), length),
[&writePipe](boost::system::error_code, size_t)
{
// writePipe.close();
});
/*
I know that this code as-is would have issues with
synchronizing `buffer`, but for the purpose of this
question please ignore that
*/
}
}
基本上我有一个内存缓冲区,我正在其中做一些工作,并且我经常想向子进程发送一些二进制数据。我的子进程看起来像这样:
孩子.cpp
#include <iostream>
#include <string_view>
void print_hex(const char* p, std::size_t size)
{
std::string_view input(p, size);
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
std::string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = static_cast<const unsigned char>(input[i]);
// output.append("0x");
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
output.append(" ");
}
if (output.size() > 0) output.pop_back();
std::cout << "HEX (" << size<< "): " << output << std::endl;
}
int main()
{
std::vector<char> buffer;
buffer.resize(BUFFER_SIZE);
bool done = false;
while (!done)
{
auto rdbuf = std::cin.rdbuf();
while (auto count = rdbuf->sgetn(buffer.data(), BUFFER_SIZE))
{
print_hex(buffer.data(), count);
}
}
}
writePipe.close()
注释掉后,我注意到我的子程序在服务器进程终止之前从未获得任何数据。如果我取消注释关闭管道的调用,那么我只能在第一次boost::asio::async_write()
调用时处理数据。
编辑:
不幸的是,@sehe 的原始答案并没有解决这个问题。我稍微更新了服务器代码以更好地说明问题(并且我解决了保留/调整大小问题)。
然而,当我再次环顾四周时,我读到了一些关于sgetn()
它的语言:
streambuf 中 xsgetn 的默认定义从受控输入序列中检索字符并将它们存储在 s 指向的数组中,直到提取了 n 个字符或到达序列的末尾。
所以,我重构了我的客户端,首先询问流有多少字节可用,然后分块读取流。这是我的第一次尝试:
bool done = false;
while (!done)
{
auto rdbuf = std::cin.rdbuf();
const auto available = rdbuf->in_avail();
if (available == 0)
{
continue;
}
auto bytesToRead = std::min(BUFFER_SIZE, static_cast<std::uint32_t>(available));
auto bytesRead = rdbuf->sgetn(buffer.data(), bytesToRead);
print_hex(buffer.data(), bytesRead);
while (bytesRead < available)
{
bytesToRead = std::min(BUFFER_SIZE, static_cast<std::uint32_t>(available - bytesRead));
bytesRead += rdbuf->sgetn(buffer.data(), bytesToRead);
print_hex(buffer.data(), bytesRead);
}
}
但是,即使在添加之后std::cin.sync_with_stdio(false);
(来自答案Why does in_avail() output zero even if the stream has some char?),调用rdbuf->in_avail()
总是返回0
。即使我尝试在我的服务器之外并在命令行上尝试,例如:ls | client
我希望我的客户端程序在数据进入时读取数据,而不必(1)关闭服务器进程或(2)关闭管道(除非我可以重新打开管道以执行后续write
()。
谢谢!