无法使用istream和ostream指针在Linux管道上写入

时间:2015-01-30 20:24:41

标签: c++ linux pipe ostream istream

首先是一些背景:

//开始背景

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

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

我最初使用s td::cout.writestd::cin.read来实现它们,并且它运行正常。但是,我想使用一个可以对消息做好事的类,并使我的实用程序代码更清晰。因此,该类会使用name字符串和istream*ostream*并将其粘贴到std::map<std::string,std::istream>std::map<std::string,std::ostream>以供日后使用。当然,它是指向std::cin的指针或指向std::cout的指针,我将其放入地图中。

然后,在util1中,我告诉它向命名流发送消息,基本上,它确实如下(是的,我使用不必要的parens,因为我是偏执狂):< / p>

// BEGIN SNIP

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();

// END SNIP

util2中,我告诉它要查找一条消息,它本质上是这样的:

// BEGIN SNIP

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);
 }

// END SNIP

好的,这就是我正在做的事情。

//结束背景

这就是谜团:

当我将第一个实用程序运行到文件cat file | util1 > msgFile然后将该文件提供给第二个实用程序cat msgFile | util2时,它可以正常工作。所以,我知道写的都是以正确的顺序发生的。但是,如果我直接在实用程序之间管道数据cat file | util1 | util2它不起作用!我似乎一遍又一遍地读取同步字而不是阅读消息(我的“这不应该是一个同步字”消息喷出,显然是每次读取)。第一个实用程序在没有投诉的情况下运行(“COMPLAINT”消息不会出现)。

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

所以,如果我使用std :: cout.write和std :: cin.read将所有这些代码直接放在我的实用程序中,它就可以工作。如果我将(istream*)&std::cin输出传输到文件并将文件传输到(ostream*)&std::cout的输入,它可以与我的地图中的util1util2一起使用,但它不会如果我将(istream*)&std::cin输出传输到(ostream*)&std::cout的输入,则可以使用我的地图中的util1util2

这让我疯了!谁能告诉我发生了什么事?

谢谢!

1 个答案:

答案 0 :(得分:0)

胜利!谢谢Barmar!

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

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

  2. 我无法在管道流上使用tellp。

  3. 所以,如果我按如下方式编写它会起作用:

    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();
    

    我仍然想知道每次调用我写了多少字节,但我想它无论是一切还是一无所有。尽管如此,我并不是百分之百地确信写不好的东西。