读取管道(C / C ++),没有错误,但不是所有数据

时间:2015-11-30 06:39:27

标签: python c++ c pipe protocol-buffers

在C ++程序中,我想获取python程序可以轻松提供的一些数据。 C ++程序调用{​​{1}},读取数据(序列化的protobuf)并继续。这很好但最近开始失败的是收到的字符串比发送的短。

我试图理解为什么我不读我写的东西(尽管没有报道错误)以及如何产生进一步的假设。 Fwiw,这是在linux(64位)上,两个进程都是本地的。 Python是2.7。

(数据大小确实很大(现在是17MB,一次500 KB),但这不应该导致失败,尽管这是一个确定的信号,我需要为了提高效率而进行一些更改。)

在python方面,我计算了group_id映射到组的一个dict(a popen(),参见下面):

RegistrationProgress

请注意payload = RegistrationProgressArray() for group_id, group in groups.items(): payload.group.add().CopyFrom(group) payload.num_entries = len(groups) print('{a}, {p}'.format(a=len(groups), p=len(payload.group)), file=sys.stderr) print(payload.SerializeToString()) print('size={s}'.format(s=len(payload.SerializeToString())), file=sys.stderr) a在python端匹配(正确!)。大小约为17MB。在C ++方面,

p

大小约为144KB。

我发送的protobuf看起来像这样。 string FetchProtoFromXXXXX<string>(const string& command_name) { ostringstream fetch_command; fetch_command << /* ... */ ; if (GetMode(kVerbose)) { cout << "FetchProtoFromXXXXX()" << endl; cout << endl << fetch_command.str() << endl << endl; } FILE* fp = popen(fetch_command.str().c_str(), "r"); if (!fp) { perror(command_name.c_str()); return ""; } // There is, sadly, no even remotely portable way to create an // ifstream from a FILE* or a file descriptor. So we do this the // C way, which is of course just fine. const int kBufferSize = 1 << 16; char c_buffer[kBufferSize]; ostringstream buffer; while (!feof(fp) && !ferror(fp)) { size_t bytes_read = fread(c_buffer, 1, kBufferSize, fp); if (bytes_read < kBufferSize && ferror(fp)) { perror("FetchProtoFromXXXXX() failed"); // Can we even continue? Let's try, but expect that it // may set us up for future sadness when the protobuf // isn't readable. } buffer << c_buffer; } if (feof(fp) && GetMode(kVerbose)) { cout << "Read EOF from pipe" << endl; } int ret = pclose(fp); const string out_buffer(buffer.str()); if (ret || GetMode(kVerbose)) { cout << "Pipe closed with exit status " << ret << endl; cout << "Read " << out_buffer.size() << " bytes." << endl; } return out_buffer; } 有点偏执,因为它应与num_entries相同,与group_size()相同。

group().size()

然后我跑的是

message RegistrationProgress { ... }

message RegistrationProgressArray {
required int32 num_entries = 1;
repeated RegistrationProgress group = 2;
}

并且运行它的输出是

array = FetchProtoFromXXXXX("my_command.py");
cout << "size=" << array.num_entries() << endl;
if (array.num_entries() != array.group_size()) {
    cout << "Something is wrong: array.num_entries() == "
         << array.num_entries()
         << " != array.group_size() == " << array.group_size()
         << " " << array.group().size()
         << endl;
    throw MyExceptionType();
}

检查反序列化的protobuf,看起来group是一个长度为1的数组,只包含我期望的数组的第一个元素。

2 个答案:

答案 0 :(得分:1)

此...

buffer << c_buffer;

...要求c_buffer包含ASCIIZ内容,但在您的情况下,您不是NUL终止它。

相反,请确保捕获读取的确切字节数(即使嵌入了NUL s):

buffer.write(c_buffer, bytes_read);

答案 1 :(得分:1)

您使用以下内容将每个块连接到输出buffer

buffer << c_buffer;

正如Tony D在他的回答中所解释的那样,在做之前你不会使c_buffer为空,所以如果c_buffer不包含嵌入的空字符,你就会调用未定义的行为。

相反,如果c_buffer确实包含嵌入的空字符,则会删除并忽略流的某些部分。

您确定流媒体协议不包含嵌入式'\0'字节吗?

您也应该阅读Why is “while ( !feof (file) )” always wrong?,但在您的情况下,我认为这不会导致您的问题。