Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IPC: Using of named pipes in c++ between two programs

I'm trying to realise a IPC between two different programs running on the same machine (in my case its a CentOS7). To have just a kind of loose coupling I decided to use a named pipe for the IPC. Therefore I'm was playing with the following example and ran into different problems.

Creating and writing into the pipe:

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

Reading the pipe:

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

I feel like named pipes are pretty unflexible in its behavior I figured out with my test programs. First of all if no reading process is attached to the fifo pipe all messages except of the last one written to the pipe get lost (or generally speaking only the last message can be read after the reading process is attached to the pipe). If you write multiple messages into the pipe all messages betweem the reading (e.g. polled) will be interpreted as one single message (I'm aware that they can be splitted by \0).

The main goals for the named pipe is a) system logs and b) kind of user authentication. The asynchronous of named pipes fits perfectly to my need. But anyway, I'm not sure if named pipes are the best solution for IPC between different programs. Also I'm not sure if the behavior descripted above is normal or if I'm using named pipe in a wrong way. I also thought about sockets but then I will run into huge blocking problems.

Thanks for your help.

like image 663
FredFloete Avatar asked Jul 20 '15 09:07

FredFloete


People also ask

How do you communicate between two C programs?

In this post, the communication between child and parent processes is done using kill() and signal(), fork() system call. fork() creates the child process from the parent. The pid can be checked to decide whether it is the child (if pid == 0) or the parent (pid = child process id).

What Is named pipes in IPC?

Named pipe is meant for communication between two or more unrelated processes and can also have bi-directional communication. Already, we have seen the one-directional communication between named pipes, i.e., the messages from the client to the server.

How IPC is implemented using pipes?

Step 1 − Create a pipe. Step 2 − Create a child process. Step 3 − Parent process writes to the pipe. Step 4 − Child process retrieves the message from the pipe and writes it to the standard output.

Can we use a pipe system call for two way communication between two related processes?

As a channel of communication a pipe operates in one direction only. Pipes cannot support broadcast i.e. sending message to multiple processes at the same time. The read end of a pipe reads any way. It does not matter which process is connected to the write end of the pipe.


2 Answers

"First of all if no reading process is attached to the fifo pipe all messages except of the last one written to the pipe get lost".

No, they don't. Use cat instead of your (lousily written :D) reading process and you'll get all of the messages.

You are right. Pipes are byte-oriented, not message oriented. If you want messages, there's other IPC for that (e.g., SysV message queues).

Beej's Guide to Unix IPC is an excellent intro to Unix ipc, if you want something more advanced, then Advanced Programming in the Unix Environment by Richard Stevens is very good.

As far as user authentication is concerned, check out Unix sockets.

like image 116
PSkocik Avatar answered Oct 19 '22 09:10

PSkocik


Looks like you're trying to use pipes for what they were not designed for. First of all, there have to be reading process "attached" for the other side of the pipe. If you try to open pipe for writing and there is no reading process, open will hang waiting for it or return -1 with errno set to ENXIO (when O_NONBLOCK flag is used). So named pipe is a kind of randez-vous point in the system, where two processes met to exchange data ;)

Second - do not treat writing to a pipe as sending a message. Pipe is rather a "stream of bytes". You can write 10 bytes once, and the reader can do 5 reads of 2 bytes each, or vice-versa: you can write 5 times 2 bytes, and the reader can read them at once. So if you plan to send some kind of "messages" over the pipe you should develop some kind of minimal transport protocol (for example use '\0' byte as a delimiter, as you mentioned above).

So, maybe you should look at System V message queues (see msgget, msgctl, msgrcv, msgsnd, or POSIX message queues (see mq_open, mq_unlink, mq_send, mq_receive etc.), which gives you possibility to send and receive 'atomic' messages.

like image 27
nsilent22 Avatar answered Oct 19 '22 10:10

nsilent22