【问题标题】:Cannot write across a Linux pipe with istream and ostream pointers无法使用 istream 和 ostream 指针跨 Linux 管道写入
【发布时间】:2015-03-30 09:48:09
【问题描述】:

先介绍一下背景:

// 开始背景

我在 x86_64 上的 CentOS 6.5 Linux 下运行,并使用 c++ (GCC) 4.4.7 20120313 进行编译。

我有几个实用程序。一个人在其标准输入上读取具有恒定大小的消息,将其转换为稍微不同的消息,然后将其写入其标准输出。另一个读取其标准输入上略有不同的消息,并将其发送到一个端口。我想执行cat file | util1 | util2 形式的命令来转换和发送存储在文件中的消息。

我最初使用 std::cout.writestd::cin.read 实现了它们,并且效果很好。但是,我想使用一个类,它可以对消息做一些整洁的事情,并使我的实用程序的代码更干净。因此,该类采用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 &gt; msgFile,然后将该文件提供给第二个实用程序 cat msgFile | util2 时,它工作得很好。所以,我知道所有的写入都以正确的顺序发生。但是,如果我直接在实用程序之间通过管道传输数据cat file | util1 | util2 它不起作用!我似乎一遍又一遍地阅读同步字而不是阅读消息(我的“这不应该是同步字”消息喷出,显然是每次阅读)。第一个实用程序运行时没有投诉(“投诉”消息不会出现)。

现在,我对 Linux 管道的理解是,首先,它们应该按顺序传递数据。其次,如果缓冲区为空,则在终点块进行读取操作。第三,如果缓冲区已满,则在源块上进行写操作。第四,他们使用 64K 缓冲区,这比我的小消息所需的方式多,所以他们不适合。所以,简而言之,我不应该看到缓冲区溢出,而且我不知道写入文件和写入管道之间还有什么不同。

所以,如果我使用 std::cout.write 和 std::cin.read 并将所有这些代码直接放在我的实用程序中,它就可以工作。如果我将util1 输出传送到文件并将文件传送到util2 的输入,则它适用于我的地图中的(istream*)&amp;std::cin(ostream*)&amp;std::cout,但它不适用于(istream*)&amp;std::cin 和@987654343如果我将util1 输出通过管道传输到util2 的输入,则@来自我的地图。

这快把我逼疯了!谁能告诉我这是怎么回事?

谢谢!

【问题讨论】:

  • 请正确格式化您的代码。这也不是一个 phpbb 论坛,所以使用 markdown 来格式化不是 phpbb 标签。
  • 我修正了你的格式,你为什么撤消它?你让它比原来的还要糟糕!
  • @Barmar 回滚到你的版本。
  • @Barmar 不小心,抱歉。手机编辑难,工作电脑登录难。
  • 我有点担心在管道流上使用tellp

标签: c++ linux pipe ostream istream


【解决方案1】:

胜利!谢谢巴尔玛!

上面的代码有两个问题;第一个很尴尬。

  1. 作者的投诉进入了一个日志文件,而我完全看错了日志文件。正确的日志文件充满了抱怨。

  2. 我不能在管道流上使用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% 确信写得不好就不会写什么。

【讨论】:

  • 我想把这篇文章归功于 Barmar,但他写的是评论而不是答案。所以,我会用这个检查问题。
猜你喜欢
  • 2011-08-31
  • 1970-01-01
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
相关资源
最近更新 更多