IPC:在两个程序之间使用命名管道的C++方法

7
我正在尝试在同一台计算机上的两个不同程序之间实现IPC(在我的情况下,是CentOS7)。为了实现松散的耦合,我决定使用一个命名管道进行IPC。因此,我正在使用以下示例并遇到了不同的问题。
创建并写入管道:
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

using namespace std;

main()  {
 int fd;
 char * myfifo = new char [12];
 strcpy(myfifo, "./tmp/myfifo1");

 /* create the FIFO (named pipe) */
 mkfifo(myfifo, 0666);
 /* write "Hi" to the FIFO */
 fd = open("./tmp/myfifo1", O_WRONLY ); //open(myfifo, O_WRONLY | O_NONBLOCK);
 if (fd == -1) {
     perror("open");
     return EXIT_FAILURE;
 } 
 printf("File open\n");
 write(fd, "entry [1]", sizeof("entry [1]"));
 sleep(1);
 write(fd, "entry [2]", sizeof("entry [2]"));
 sleep(2);
 write(fd, "entry [3]", sizeof("entry [3]"));
 printf("Content written\n");
 close(fd);
 printf("Connection closed\n");
 /* remove the FIFO */
 unlink(myfifo);
 return 0;
}

读取管道:

#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <string>
#include <iostream>

using namespace std;

main()  {
 int fd; 
 fd_set set_a;

 char * myfifo = new char [12];
 strcpy(myfifo, "./tmp/myfifo1");
 char buffer[1024];

 fd = open("./tmp/myfifo1", O_RDONLY | O_NONBLOCK);
 if (fd == -1) {
     perror("open");
     return EXIT_FAILURE;
 } 
 ssize_t bytes;
 size_t total_bytes = 0;
 printf("\nDropped into read pipe\n");
 while(1){
     if((bytes = read(fd, buffer, sizeof(buffer))) > 0){
         std::string message(&buffer[22]);
         total_bytes += (size_t)bytes;
         printf("%i", bytes);

         printf("Message: %s\n", message.c_str());
         memset(&buffer[0], 0, sizeof(buffer));
     }else{
         if (errno == EWOULDBLOCK) {
             printf("\ndone reading (%d bytes)\n", (int)total_bytes);
             //break;
         }
         printf("No message\n");
         sleep(2);
     } 
 }
 return EXIT_SUCCESS;
}

我觉得命名管道在行为上相当不灵活,通过我的测试程序我已经找出了其中的问题。首先,如果没有任何读进程附加到FIFO管道,那么除了最后一个写入管道的消息外,所有消息都会丢失(或者一般来说,只有在读进程附加到管道后才能读取最后一个消息)。如果您将多个消息写入管道,则在读取(例如轮询)之间的所有消息都将被解释为单个消息(我知道它们可以通过 \0 分割)。

命名管道的主要目标是a)系统日志和b)某种形式的用户身份验证。命名管道的异步特性非常适合我的需求。但无论如何,我不确定命名管道是否是不同程序之间进行IPC的最佳解决方案。此外,我也不确定上述描述的行为是正常的,还是我使用命名管道的方式有误。我也考虑过套接字,但这样会遇到巨大的阻塞问题。

感谢您的帮助。


请查看以下问题:http://stackoverflow.com/q/22714816/540286 - Ortwin Angermeier
解耦 + 异步消息 + 持久化 = 消息传递。尝试使用消息传递解决方案 - 看看 RabbitMQ,它已经包含在当前的 CentOS 发行版中。 - Sigi
2个回答

6
“首先,如果fifo管道未连接读取进程,则管道中除最后一个写入的消息之外的所有消息都会丢失。”
不,它们并没有。使用cat而不是你(拙劣地编写的:D)读取过程,你将得到所有消息。
你是对的。管道是面向字节的,而不是面向消息的。如果你想要消息,还有其他IPC可用(例如SysV消息队列)。
如果你想了解更高级的内容,那么Unix IPC入门指南Beej's Guide to Unix IPC非常好。至于用户身份验证,可以查看Unix套接字。

谢谢回复。如果您将套接字的读写传输到自己的线程中,我不关心阻塞与非阻塞。对我来说,套接字的主要问题是如何从线程向父级获取“异步”回调,如果消息到达。因此,我开始使用管道。 - FredFloete
1
@i7clock 完成。 - Petr Skocik

4
看起来你正在试图使用不适合于管道的管道。首先,另一端的管道必须与读取进程“连接”。如果您尝试打开写入管道并且没有读取进程,则open将挂起等待它或返回-1,并将errno设置为ENXIO(使用O_NONBLOCK标志时)。因此,命名管道是系统中的一种约会点,在这里两个进程相遇以交换数据 ;)
其次,请勿将向管道写入视为发送消息。管道更像是“字节流”。您可以一次写入10个字节,读取器可以进行5次2个字节的读取,或者反之:您可以写入5次2个字节,读取器可以立即读取它们。因此,如果您计划通过管道发送某种“消息”,则应开发某种最小传输协议(例如使用'\0'字节作为分隔符,如您上面提到的)。
因此,也许您应该查看System V消息队列(请参见msggetmsgctlmsgrcvmsgsnd),或POSIX消息队列(请参见mq_openmq_unlinkmq_sendmq_receive等),这使您能够发送和接收“原子”消息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接