无法使用 istream 和 ostream 指针写入 Linux 管道
Cannot write across a Linux pipe with istream and ostream pointers
首先是一些背景:
// 开始背景
我 运行在 x86_64 上的 CentOS 6.5 Linux 下,并使用 c++ (GCC) 4.4.7 20120313 进行编译。
我有几个实用程序。一个在其 stdin 上读取具有恒定大小的消息,将其转换为稍微不同的消息,并将其写入其 stdout。另一个读取其标准输入中略有不同的消息,并将其发送到端口。我想执行 cat file | util1 | util2
形式的命令来转换和发送存储在文件中的消息。
我最初使用 std::cout.write
和 std::cin.read
实现了它们,并且效果很好。但是,我想使用 class 来处理消息并使我的实用程序代码更清晰。因此,class 需要一个 name
字符串和一个 istream*
或 ostream*
并将它们粘贴在 std::map<std::string,std::istream>
或 std::map<std::string,std::ostream>
中以备后用.当然,它是指向 std::cin
的指针或指向 std::cout
的指针,我将其放入地图中。
然后,在 util1
中,我告诉它向命名流发送一条消息,它基本上执行以下操作(是的,我使用了不必要的括号,因为我很偏执) :
// 开始截图
std::ostream *o_file = (o_file_map[name_string]);
uint32_t sync = fromSomewhere();
uint32_t msgLen = sizeof(msg);
size_t outPos = o_file->tellp();
size_t nxtPos = o_file->write((char*)&sync,sizeof(sync)).tellp();
bool result = ((nxtPos - outPos) == sizeof(sync));
if (result) {
outPos = o_file->tellp();
nxtPos = o_file->write((char*)&msgLen,sizeof(msgLen)).tellp();
result = ((nxtPos - outPos) == sizeof(msgLen));
} else {
std::err << "COMPLAINT1!" << std::endl;
}
if (result) {
outPos = o_file->tellp();
nxtPos = o_file->write((char*)&msg,msgLen).tellp();
result = ((nxtPos - outPos) == msgLen);
} else {
std::err << "COMPLAINT2!" << std::endl;
}
if (!result) {
std::err << "COMPLAINT3!" << std::endl;
}
o_file->flush();
// 结束片段
在 util2
中,我告诉它查找消息,它确实这样做了,基本上是这样的:
// 开始截图
std::istream *i_file = (i_file_map[name_string]);
uint32_t tstSync = 0;
uint32_t msgSize = 0; // Yeah, I called it msgLen before.
bool ok = true;
bool do_flip = false;
size_t readLen = 0;
if (*i_file) {
readLen = i_file->read((char*)&tstSync, sizeof(tstSync)).gcount();
ok = (readLen / sizeof(tstSync) > 0);
}
if (ok && (*i_file)) {
readLen = i_file->read((char*)&msgSize, sizeof(msgSize)).gcount();
ok = (readLen / sizeof(msgSize) > 0);
}
if (ok) {
if (!isSync(tstSync)) {
if (isFlippedSync(tstSync)) {
do_flip = true;
} else {
ok = false;
}
}
if (isSync(msgSize)) {
std::cerr << "This is not supposed to be a sync word" << std::endl;
ok = false;
}
if (do_flip) {
msgSize = flip(msgSize);
}
}
if (ok) {
Size_msg_buf_to(msgSize); // enlarges object's buffer iff it's too small.
// it would throw an exception if it failed.
}
if (ok && (*i_file)) {
readLen = i_file->read(msg_buf, msgSize).gcount();
ok = (readLen / msgSize > 0);
}
// 结束片段
好的,这就是我正在做的。
// 结束背景
这是谜:
当我 运行 将第一个实用程序添加到文件 cat file | util1 > msgFile
然后将该文件提供给第二个实用程序 cat msgFile | util2
时,它工作得很好。所以,我知道写入都是按正确的顺序进行的。但是,如果我直接在实用程序之间传输数据 cat file | util1 | util2
,它就不起作用了!我似乎一遍又一遍地阅读同步词而不是阅读消息(我的 "This is not supposed to be a sync word" 消息喷出,显然是每次阅读)。第一个实用程序 运行s 没有投诉("COMPLAINT" 消息不出来)。
现在,我对 Linux 管道的理解是,首先,它们应该按顺序传送数据。其次,如果缓冲区为空,则在终点块进行读取操作。第三,如果缓冲区已满,则在源块处进行写操作。第四,他们使用 64K 缓冲区,这比我的小消息需要的多 way,所以他们不适合。所以,简而言之,我不应该看到缓冲区溢出,而且我不知道写入文件和写入管道之间还有什么不同。
因此,如果我使用 std::cout.write 和 std::cin.read 并将所有这些代码直接放在我的实用程序中,它就会起作用。如果我将 util1
输出通过管道传输到文件并将文件通过管道传输到 util2
的输入,它可以与我的地图中的 (istream*)&std::cin
和 (ostream*)&std::cout
一起使用,但它不会如果我将 util1
输出通过管道传输到 util2
.
的输入,则可以使用我的地图中的 (istream*)&std::cin
和 (ostream*)&std::cout
这让我抓狂!谁能告诉我这是怎么回事?
谢谢!
胜利!谢谢 Barmar!
上面的代码有两个问题;第一个很尴尬
作者的投诉进入了日志文件,我完全看错了日志文件。正确的日志文件充满了抱怨。
我不能在管道流上使用 tellp。
所以,如果我这样写就可以了:
std::ostream *o_file = (o_file_map[name_string]);
uint32_t sync = fromSomewhere();
uint32_t msgLen = sizeof(msg);
size_t outPos = o_file->tellp();
bool result = (o_file->write((char*)&sync,sizeof(sync))).good();
if (result) {
result = (o_file->write((char*)&msgLen,sizeof(msgLen))).good();
} else {
std::err << "COMPLAINT1!" << std::endl;
}
if (result) {
result = (o_file->write((char*)&msg,msgLen)).good();
} else {
std::err << "COMPLAINT2!" << std::endl;
}
if (!result) {
std::err << "COMPLAINT3!" << std::endl;
}
o_file->flush();
我仍然想知道我每次调用写入了多少字节,但我想要么全部写入,要么什么都不写入。不过,我不是 100% 相信写得不好就什么也没写。
首先是一些背景:
// 开始背景
我 运行在 x86_64 上的 CentOS 6.5 Linux 下,并使用 c++ (GCC) 4.4.7 20120313 进行编译。
我有几个实用程序。一个在其 stdin 上读取具有恒定大小的消息,将其转换为稍微不同的消息,并将其写入其 stdout。另一个读取其标准输入中略有不同的消息,并将其发送到端口。我想执行 cat file | util1 | util2
形式的命令来转换和发送存储在文件中的消息。
我最初使用 std::cout.write
和 std::cin.read
实现了它们,并且效果很好。但是,我想使用 class 来处理消息并使我的实用程序代码更清晰。因此,class 需要一个 name
字符串和一个 istream*
或 ostream*
并将它们粘贴在 std::map<std::string,std::istream>
或 std::map<std::string,std::ostream>
中以备后用.当然,它是指向 std::cin
的指针或指向 std::cout
的指针,我将其放入地图中。
然后,在 util1
中,我告诉它向命名流发送一条消息,它基本上执行以下操作(是的,我使用了不必要的括号,因为我很偏执) :
// 开始截图
std::ostream *o_file = (o_file_map[name_string]);
uint32_t sync = fromSomewhere();
uint32_t msgLen = sizeof(msg);
size_t outPos = o_file->tellp();
size_t nxtPos = o_file->write((char*)&sync,sizeof(sync)).tellp();
bool result = ((nxtPos - outPos) == sizeof(sync));
if (result) {
outPos = o_file->tellp();
nxtPos = o_file->write((char*)&msgLen,sizeof(msgLen)).tellp();
result = ((nxtPos - outPos) == sizeof(msgLen));
} else {
std::err << "COMPLAINT1!" << std::endl;
}
if (result) {
outPos = o_file->tellp();
nxtPos = o_file->write((char*)&msg,msgLen).tellp();
result = ((nxtPos - outPos) == msgLen);
} else {
std::err << "COMPLAINT2!" << std::endl;
}
if (!result) {
std::err << "COMPLAINT3!" << std::endl;
}
o_file->flush();
// 结束片段
在 util2
中,我告诉它查找消息,它确实这样做了,基本上是这样的:
// 开始截图
std::istream *i_file = (i_file_map[name_string]);
uint32_t tstSync = 0;
uint32_t msgSize = 0; // Yeah, I called it msgLen before.
bool ok = true;
bool do_flip = false;
size_t readLen = 0;
if (*i_file) {
readLen = i_file->read((char*)&tstSync, sizeof(tstSync)).gcount();
ok = (readLen / sizeof(tstSync) > 0);
}
if (ok && (*i_file)) {
readLen = i_file->read((char*)&msgSize, sizeof(msgSize)).gcount();
ok = (readLen / sizeof(msgSize) > 0);
}
if (ok) {
if (!isSync(tstSync)) {
if (isFlippedSync(tstSync)) {
do_flip = true;
} else {
ok = false;
}
}
if (isSync(msgSize)) {
std::cerr << "This is not supposed to be a sync word" << std::endl;
ok = false;
}
if (do_flip) {
msgSize = flip(msgSize);
}
}
if (ok) {
Size_msg_buf_to(msgSize); // enlarges object's buffer iff it's too small.
// it would throw an exception if it failed.
}
if (ok && (*i_file)) {
readLen = i_file->read(msg_buf, msgSize).gcount();
ok = (readLen / msgSize > 0);
}
// 结束片段
好的,这就是我正在做的。
// 结束背景
这是谜:
当我 运行 将第一个实用程序添加到文件 cat file | util1 > msgFile
然后将该文件提供给第二个实用程序 cat msgFile | util2
时,它工作得很好。所以,我知道写入都是按正确的顺序进行的。但是,如果我直接在实用程序之间传输数据 cat file | util1 | util2
,它就不起作用了!我似乎一遍又一遍地阅读同步词而不是阅读消息(我的 "This is not supposed to be a sync word" 消息喷出,显然是每次阅读)。第一个实用程序 运行s 没有投诉("COMPLAINT" 消息不出来)。
现在,我对 Linux 管道的理解是,首先,它们应该按顺序传送数据。其次,如果缓冲区为空,则在终点块进行读取操作。第三,如果缓冲区已满,则在源块处进行写操作。第四,他们使用 64K 缓冲区,这比我的小消息需要的多 way,所以他们不适合。所以,简而言之,我不应该看到缓冲区溢出,而且我不知道写入文件和写入管道之间还有什么不同。
因此,如果我使用 std::cout.write 和 std::cin.read 并将所有这些代码直接放在我的实用程序中,它就会起作用。如果我将 util1
输出通过管道传输到文件并将文件通过管道传输到 util2
的输入,它可以与我的地图中的 (istream*)&std::cin
和 (ostream*)&std::cout
一起使用,但它不会如果我将 util1
输出通过管道传输到 util2
.
(istream*)&std::cin
和 (ostream*)&std::cout
这让我抓狂!谁能告诉我这是怎么回事?
谢谢!
胜利!谢谢 Barmar!
上面的代码有两个问题;第一个很尴尬
作者的投诉进入了日志文件,我完全看错了日志文件。正确的日志文件充满了抱怨。
我不能在管道流上使用 tellp。
所以,如果我这样写就可以了:
std::ostream *o_file = (o_file_map[name_string]);
uint32_t sync = fromSomewhere();
uint32_t msgLen = sizeof(msg);
size_t outPos = o_file->tellp();
bool result = (o_file->write((char*)&sync,sizeof(sync))).good();
if (result) {
result = (o_file->write((char*)&msgLen,sizeof(msgLen))).good();
} else {
std::err << "COMPLAINT1!" << std::endl;
}
if (result) {
result = (o_file->write((char*)&msg,msgLen)).good();
} else {
std::err << "COMPLAINT2!" << std::endl;
}
if (!result) {
std::err << "COMPLAINT3!" << std::endl;
}
o_file->flush();
我仍然想知道我每次调用写入了多少字节,但我想要么全部写入,要么什么都不写入。不过,我不是 100% 相信写得不好就什么也没写。