我的 C++ 程序如何等到对命名管道进行新的写入?

Posted

技术标签:

【中文标题】我的 C++ 程序如何等到对命名管道进行新的写入?【英文标题】:How can my C++ program wait until a new write has been made to the named pipe? 【发布时间】:2021-03-18 14:43:40 【问题描述】:

我在 C++ 中有两个独立的程序,一个以不可预测的间隔写入两个命名管道,另一个应该等待从管道中读取新内容,只要可用。为简单起见,这里我的作者只写了两次管道(第一次:“One”“Tree”,第二次:“Two”“Fogs”)。

我的作家计划是:

#include <unistd.h>
#include <iostream>

int main()

    int fd1, fd2;
    const char* myfifo1 = "/tmp/myfifo1";
    const char* myfifo2 = "/tmp/myfifo2";

    mkfifo(myfifo1, 0666);
    mkfifo(myfifo2, 0666);

    fd1 = open(myfifo1, O_WRONLY);
    fd2 = open(myfifo2, O_WRONLY);

    write(fd1, "One", sizeof("One"));
    write(fd2, "Tree", sizeof("Tree"));

    sleep(5);

    write(fd1, "Two", sizeof("Two"));
    write(fd2, "Fogs", sizeof("Fogs"));

    close(fd1);
    close(fd2);

    unlink(myfifo1);
    unlink(myfifo2);
   

我的阅读器程序是:

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAX_BUF 1024

int main()

    int fd1, fd2;
    const char * myfifo1 = "/tmp/myfifo1";
    const char * myfifo2 = "/tmp/myfifo2";
    char buf1[MAX_BUF], buf2[MAX_BUF];
    
    fd1 = open(myfifo1, O_RDONLY);
    fd2 = open(myfifo2, O_RDONLY);
    
    while (1) 
        read(fd1, buf1, MAX_BUF);
        read(fd2, buf2, MAX_BUF);
        
        printf("Received: %s\n", buf1);
        printf("Received: %s\n", buf2);
    

    close(fd1);
    close(fd2);
    return 0;

我不希望读取器终止或关闭读取之间的连接,我需要它保持活动状态并等待写入器在命名管道中写入新内容。 但是,通过同时运行这两个程序(在不同的内核中,首先是编写器,然后是读取器),我得到:

Received: One
Received: Tree
Received: Two
Received: Fogs
Received: Two
Received: Fogs
Received: Two
Received: Fogs
Received: Two
Received: Fogs
      ...
      ...

想要的行为是:

Received: One
Received: Tree
Received: Two
Received: Fogs

然后什么都没有(读者应该等待另一个写入)。

我知道我应该在读取缓冲区后以某种方式清除它,但这不是读取的默认行为吗?既然读完了《二》(还有《迷雾》),为什么还要一直读,不等新内容呢?

我应该进行哪些修改?

非常感谢您!

【问题讨论】:

管道被写入程序破坏。没有什么可以等待的。 read 不再阅读旧消息,它出错了。如果您检查每个 I/O 操作的结果,您就会知道,这是您应该做的。 您可能也有兴趣查看select,而不是让您的读者忙于等待新消息。 读出错误,你的程序不会寻找错误,因为出现错误,buf1和buf2的内容没有更新,所以你的程序打印旧的内容。 您的帖子被标记为 C++,但代码是 C。您的意思是将其标记为 C 吗? @NathanPierson 它是阻塞读取而不是忙等待。但稍微复杂一点的应用程序会使用select() 同时阻塞多个管道,以便其中一个可用。 【参考方案1】:

关闭管道将文件结束信号发送到另一端。

read 在遇到文件结束条件时将读取零字节(并返回零)。文件结尾之后的任何后续读取都是空操作。

如果要继续从同一个管道读取,需要检查read的结果,如果返回零,关闭管道再打开。

如果您需要其他人继续使用它,当然不应该unlink 管道。

还有一点需要注意:read (1) 不会以 0 字节终止缓冲区,并且 (2) 不一定会读取单个 write 写入的确切字节数。您应该自己检测消息结束,而不是依靠readwrite 为您完成。 read 完全有可能读取字符串的一半而没有终止空字符,然后您的 printf 将打印垃圾。

【讨论】:

当阅读器不知道应该读取的字节数时,如何检测到消息结束?一种方法是作者计算此数字并在消息之前将其发送给读者。还有另一种更简单的方法吗?请帮忙。谢谢。 API 中没有消息。您需要设计一个实现消息的协议。在消息长度之后发送消息是一种可能的方法。如果要发送 C 字符串,您可能希望在发送端发送一个 0 终止符并在接收端 检测它,这是另一种方式。

以上是关于我的 C++ 程序如何等到对命名管道进行新的写入?的主要内容,如果未能解决你的问题,请参考以下文章

在断开连接之前等待读取命名管道

Linux。 Python。从命名管道读取

命名管道上的 WriteFile 有时会返回 ERROR_NO_DATA

WriteFile() 块(通过命名管道从 C++ 客户端写入 C# 服务器)

使用命名管道将图像从 C++ 发送到 C# 应用程序

使用命名管道的两种 C++ 到 C# 通信