Comunicação entre processos – PIPES em C++
Autor: Eduardo A Bortolini | Publicado: 2012-02-25T16:55:00-02:00

Sistema operacional utilizado: Linux debian 2.6.32-5-amd64
O nosso caso de exemplo será com o que chamamos de "Named PIPES" também conhecidas como FIFO. Onde a utilizaremos para comunicação entre dois processos. Basicamente, teremos um servidor, que criará uma PIPE/FIFO onde o cliente escreverá um comando. O Servidor lerá essa mensagem, e escreverá na PIPE/FIFO do cliente a resposta do comando. Ou seja, o cliente também terá que criar sua PIPE para que o servidor possa escrever. Então, utilizaremos duas PIPES, uma criada pelo Server e a outra criada pelo cliente.  
A Pipe criada pelo servidor deve ser conhecida antes de rodarmos o cliente. O cliente irá criar sua Pipe pelo PID do processo e irá transmitir o nome da Pipe para o servidor através da PIPE do server, assim o servidor será capaz de abrir a Pipe do cliente e escrever o resultado nela.
No nosso exemplo vamos usar três arquivos de codificação.
Main.cpp – Esse arquivo conterá a codificação para o nosso servidorclient.cpp – Esse arquivo conterá a codificação para o nosso clienteheader_fifo.h – Esse arquivo conterá a implementação de nossa classe.
Teremos tamém um quarto arquivo, um Makefile, que será responsável por compilar nosso exemplo.
Teremos duas opções de compilação, uma para Debug, onde você poderá ver as mensagens sendo trocadas entre cliente e servidor, o que será útil para modificar ou entender melhor o processo de comunicação.
Então vamos ao código de nosso servidor:

#include "fifo_header.h"

int main(int argc, char *argv[])
{
&nbsp; &nbsp; int processID;
&nbsp; &nbsp; if(argv[1]!=NULL)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; if(strcmp("stop",argv[1]) ==0)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; system("killall PipeServer");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; else
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(strcmp("Debug",argv[1]) !=0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; processID = fork();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (processID &lt; 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_FAILURE);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (processID &gt; 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exit(EXIT_SUCCESS);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; FIFO myFifo, clientFifo;
&nbsp; &nbsp; int PipeToWrite, PipeToRead, returnedValue, ready = 0;
&nbsp; &nbsp; data buffer;
&nbsp; &nbsp; myFifo.setName(argv[1]);
&nbsp; &nbsp; myFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));
#ifdef DEBUG
&nbsp; &nbsp; cout &lt;&lt; "Name of FIFO: " &lt;&lt; myFifo.getName() &lt;&lt; endl;
&nbsp; &nbsp; cout &lt;&lt; "Path of FIFO: " &lt;&lt;myFifo.getPath() &lt;&lt; endl;
&nbsp; &nbsp; cout.flush();
#endif

&nbsp; &nbsp; /*
&nbsp; &nbsp; Vamos iniciar a criação de nossa PIPE
&nbsp; &nbsp; */
&nbsp; &nbsp; mknod(myFifo.getName(), S_IFIFO | 0666, 0);

&nbsp; &nbsp; /*
&nbsp; &nbsp; Vamos abrir a nossa PIPE que usaremos para ler do cliente
&nbsp; &nbsp; */

&nbsp; &nbsp; returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);
&nbsp; &nbsp; if(returnedValue == -1)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; printf("\n\tError in open PIPE\n");
&nbsp; &nbsp; &nbsp; &nbsp; perror(myFifo.getMessage());
&nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; }
&nbsp; &nbsp; myFifo.pipeDescriptor = returnedValue;

&nbsp; &nbsp; while (true)
&nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; returnedValue = myFifo.readFromPipe(clientFifo);

&nbsp; &nbsp; &nbsp; &nbsp; if(returnedValue &gt; 0)
&nbsp; &nbsp; &nbsp; &nbsp; {
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"retuned value from read: "&lt;&lt;returnedValue &lt;&lt;endl;
&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"What Client said is: " &lt;&lt;myFifo.getMessage()&lt;&lt;endl;
#endif
&nbsp; &nbsp; &nbsp; &nbsp; ready=0;
&nbsp; &nbsp; &nbsp; &nbsp; do
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; returnedValue = open(clientFifo.getName(), O_WRONLY/*|O_NDELAY*/);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(returnedValue == -1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(clientFifo.getName());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"Pipe client Name: "&lt;&lt; clientFifo.getName() &lt;&lt;endl;
#endif
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; clientFifo.pipeDescriptor = returnedValue;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; myFifo.writeToPipe(clientFifo);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ready=1;


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }while(!ready);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; clientFifo.closePipe();
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; return 1;
}
Agora o código de nosso cliente

#include "fifo_header.h"

int main(int argc, char *argv[])
{
&nbsp; &nbsp; /*
&nbsp; &nbsp; argv deve ser o mesmo do servidor.
&nbsp; &nbsp; Assim termos como informar o nome da PIPE e o local onde o servidor
&nbsp; &nbsp; a criou
&nbsp; &nbsp; */
&nbsp; &nbsp; FIFO myFifo, serverFifo;
&nbsp; &nbsp; data buffer;
&nbsp; &nbsp; int numberOfBytes, returnedValue, readOK;
&nbsp; &nbsp; serverFifo.setName(argv[1]);
&nbsp; &nbsp; serverFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));

#ifdef DEBUG
&nbsp; &nbsp; cout &lt;&lt; "Name of FIFO: " &lt;&lt; myFifo.getName() &lt;&lt; endl;
&nbsp; &nbsp; cout &lt;&lt; "Path of FIFO: " &lt;&lt;myFifo.getPath() &lt;&lt; endl;
&nbsp; &nbsp; cout.flush();
#endif
&nbsp; &nbsp; sprintf(buffer.name, "/home/eduardo/Documents/programas/FIFO/%d", getpid( ));
&nbsp; &nbsp; myFifo.setName(buffer.name);

&nbsp; &nbsp; if (mknod(myFifo.getName(), S_IFIFO | 0666, 0) &lt; 0)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; perror(myFifo.getName());
&nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; }
&nbsp; &nbsp; if ((returnedValue = open(serverFifo.getName(), O_WRONLY)) == -1)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; perror(serverFifo.getName());
&nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; }
&nbsp; &nbsp; serverFifo.pipeDescriptor = returnedValue;
&nbsp; &nbsp; while(true)
&nbsp; &nbsp; {

&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt; "\ncmd" &lt;&lt;endl;
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"PIPE of server: " &lt;&lt; serverFifo.getName() &lt;&lt;endl;
#endif
&nbsp; &nbsp; &nbsp; &nbsp; numberOfBytes = read(fileno(stdin), buffer.message, PIPE_BUF);
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"what you write: "&lt;&lt;buffer.message&lt;&lt;endl;
#endif
&nbsp; &nbsp; &nbsp; &nbsp; if (!strncmp("exit", buffer.message, numberOfBytes - 1))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;

&nbsp; &nbsp; &nbsp; &nbsp; write(serverFifo.pipeDescriptor,(char *) &amp;buffer, sizeof(buffer));

&nbsp; &nbsp; &nbsp; &nbsp; returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);

&nbsp; &nbsp; &nbsp; &nbsp; if(returnedValue == -1)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(myFifo.getName());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; myFifo.pipeDescriptor = returnedValue;
&nbsp; &nbsp; &nbsp; &nbsp; readOK = 0;
&nbsp; &nbsp; &nbsp; &nbsp; while (readOK != 1)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if((numberOfBytes = read(myFifo.pipeDescriptor, buffer.message, PIPE_BUF)) &gt; 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; write(fileno(stdout), buffer.message, numberOfBytes);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memset(buffer.message,0x0,PIPE_BUF);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readOK=1;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }


&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; myFifo.closePipe();


&nbsp; &nbsp; }

&nbsp; &nbsp; serverFifo.closePipe();
&nbsp; &nbsp; return 1;
}


Agora a implementação de nossa classe


#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;
#include &lt;linux/limits.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;fstream&gt;
#include &lt;errno.h&gt;
using namespace std;

typedef struct dataSet
{
&nbsp; &nbsp; &nbsp; &nbsp; char name[255];
&nbsp; &nbsp; &nbsp; &nbsp; char path[255];
&nbsp; &nbsp; &nbsp; &nbsp; char message[PIPE_BUF];
}data;

class FIFO
{
&nbsp; &nbsp; private:
&nbsp; &nbsp; &nbsp; &nbsp; data info;
&nbsp; &nbsp; public:
&nbsp; &nbsp; &nbsp; &nbsp; int pipeDescriptor;
&nbsp; &nbsp; &nbsp; &nbsp; char* getName();
&nbsp; &nbsp; &nbsp; &nbsp; void setName(char*);
&nbsp; &nbsp; &nbsp; &nbsp; char* getPath();
&nbsp; &nbsp; &nbsp; &nbsp; void setPath(char*);
&nbsp; &nbsp; &nbsp; &nbsp; char* getMessage();
&nbsp; &nbsp; &nbsp; &nbsp; void writeMessage(char*);
&nbsp; &nbsp; &nbsp; &nbsp; void closePipe();
&nbsp; &nbsp; &nbsp; &nbsp; int writeToPipe(FIFO&amp;);
&nbsp; &nbsp; &nbsp; &nbsp; int readFromPipe(FIFO&amp;);

};

char* FIFO::getName()
{
&nbsp; &nbsp; return info.name;
}

void FIFO::setName(char * nameToSet)
{
&nbsp; &nbsp; strcpy(info.name,nameToSet);
}

char* FIFO::getPath()
{
&nbsp; &nbsp; return info.path;
}

void FIFO::setPath(char *pathToSet)
{
&nbsp; &nbsp; strcpy(info.path, pathToSet);
}


char* FIFO::getMessage()
{
&nbsp; &nbsp; return info.message;
}

void FIFO::writeMessage(char *messageToWrite)
{
&nbsp; &nbsp;strcpy(info.message, messageToWrite);
}

int FIFO::writeToPipe(FIFO&amp; pipeToWrite)
{
&nbsp; &nbsp; FILE * result;
&nbsp; &nbsp; int readFromFile, numberOfBytes, popenIsOk;
&nbsp; &nbsp; char buffer[PIPE_BUF], stringBuff[PIPE_BUF];
#ifdef DEBUG
&nbsp; &nbsp; cout &lt;&lt;"what will be the command to sheel: " &lt;&lt; getMessage()&lt;&lt;endl;
#endif
&nbsp; &nbsp; result = popen(getMessage(), "r");
&nbsp; &nbsp; if(!result)
&nbsp; &nbsp; {
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"result is null" &lt;&lt; endl;
&nbsp; &nbsp; &nbsp; &nbsp; perror("errot in open file");
#endif
&nbsp; &nbsp; &nbsp; &nbsp; strcpy(stringBuff,"Command not found");
&nbsp; &nbsp; &nbsp; &nbsp; write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
&nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; }


&nbsp; &nbsp; popenIsOk=0;
&nbsp; &nbsp; while(popenIsOk!=1)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; if ((numberOfBytes = read(fileno(result), buffer, PIPE_BUF))&gt;0)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strcpy(stringBuff,buffer);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memset(buffer, 0x0, PIPE_BUF);
#ifdef DEBUG
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout &lt;&lt;"what will be written to client: " &lt;&lt; stringBuff &lt;&lt; endl;
#endif
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; popenIsOk=1;

&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; pclose(result);

}

int FIFO::readFromPipe(FIFO&amp; pipe)
{

&nbsp; &nbsp; int returnedValue;
&nbsp; &nbsp; data buffer;
&nbsp; &nbsp; returnedValue = read(pipeDescriptor, (char *) &amp;buffer, sizeof(buffer));
&nbsp; &nbsp; if(returnedValue &lt;=0 )
&nbsp; &nbsp; &nbsp; &nbsp; return returnedValue;
&nbsp; &nbsp; else
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; writeMessage(buffer.message);
&nbsp; &nbsp; &nbsp; &nbsp; pipe.setName(buffer.name);
&nbsp; &nbsp; }
&nbsp; &nbsp; return returnedValue;
}

void FIFO::closePipe()
{
&nbsp; &nbsp; close(pipeDescriptor);
}

Agora vamos ao nosso Makefile


CC:=g++

all:PipeServer Client

PipeServer:main.cpp fifo_header.h
 $(CC) -o PipeServer main.cpp

Client:client.cpp fifo_header.h
 $(CC) -o Client client.cpp

Debug: main.cpp client.cpp fifo_header.h
 $(CC) -D DEBUG -o PipeServer main.cpp
 $(CC) -D DEBUG -o Client client.cpp

Comandos necessários para compilar e rodar:Para compilar os dois programas em modo normal:&nbsp;Comando: make all
Para executar:Comando para Rodar o Server: ./PipeServer FIFO&nbsp;/home/eduardo/Documents/programas/FIFOOnde:&nbsp;FIFO é o Nome da PIPE a ser Criada
&nbsp;/home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela vai ser criada
Comando para Rodar o cliente:&nbsp;./client FIFO /home/eduardo/Documents/programas/FIFOFIFO é o Nome da PIPE criada pelo server
&nbsp;/home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela foi &nbsp;criada
Como o Server roda em BackGround, se você desejar para o server dê o comando: ./PipeServer StopPara parar o cliente apenas digite exit
Para o dois programas em modo de DEBUG
comando para compilar: make Debug
Para rodar o server(agora ele não roda em BackGround Ctrl+C se desejar parar o server):&nbsp;./PipeServer Debug /home/eduardo/Documents/programas/FIFO
Para rodar o cliente:&nbsp;./client&nbsp; Debug&nbsp;&nbsp;/home/eduardo/Documents/programas/FIFO
Bons Testes!!!!