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 servidor
client.cpp – Esse arquivo conterá a codificação para o nosso cliente
header_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[])
{
int processID;
if(argv[1]!=NULL)
{
if(strcmp("stop",argv[1]) ==0)
{
system("killall PipeServer");
return 1;
}
else
{
if(strcmp("Debug",argv[1]) !=0)
{
processID = fork();
if (processID < 0)
{
exit(EXIT_FAILURE);
}
else
{
if (processID > 0)
{
exit(EXIT_SUCCESS);
}
}
}
}
}
FIFO myFifo, clientFifo;
int PipeToWrite, PipeToRead, returnedValue, ready = 0;
data buffer;
myFifo.setName(argv[1]);
myFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));
#ifdef DEBUG
cout << "Name of FIFO: " << myFifo.getName() << endl;
cout << "Path of FIFO: " <<myFifo.getPath() << endl;
cout.flush();
#endif
/*
Vamos iniciar a criação de nossa PIPE
*/
mknod(myFifo.getName(), S_IFIFO | 0666, 0);
/*
Vamos abrir a nossa PIPE que usaremos para ler do cliente
*/
returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);
if(returnedValue == -1)
{
printf("\n\tError in open PIPE\n");
perror(myFifo.getMessage());
return 1;
}
myFifo.pipeDescriptor = returnedValue;
while (true)
{
returnedValue = myFifo.readFromPipe(clientFifo);
if(returnedValue > 0)
{
#ifdef DEBUG
cout <<"retuned value from read: "<<returnedValue <<endl;
cout <<"What Client said is: " <<myFifo.getMessage()<<endl;
#endif
ready=0;
do
{
returnedValue = open(clientFifo.getName(), O_WRONLY/*|O_NDELAY*/);
if(returnedValue == -1)
{
perror(clientFifo.getName());
return 1;
}
#ifdef DEBUG
cout <<"Pipe client Name: "<< clientFifo.getName() <<endl;
#endif
clientFifo.pipeDescriptor = returnedValue;
myFifo.writeToPipe(clientFifo);
ready=1;
}while(!ready);
clientFifo.closePipe();
}
}
return 1;
}
Agora o código de nosso cliente
#include "fifo_header.h"
int main(int argc, char *argv[])
{
/*
argv deve ser o mesmo do servidor.
Assim termos como informar o nome da PIPE e o local onde o servidor
a criou
*/
FIFO myFifo, serverFifo;
data buffer;
int numberOfBytes, returnedValue, readOK;
serverFifo.setName(argv[1]);
serverFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));
#ifdef DEBUG
cout << "Name of FIFO: " << myFifo.getName() << endl;
cout << "Path of FIFO: " <<myFifo.getPath() << endl;
cout.flush();
#endif
sprintf(buffer.name, "/home/eduardo/Documents/programas/FIFO/%d", getpid( ));
myFifo.setName(buffer.name);
if (mknod(myFifo.getName(), S_IFIFO | 0666, 0) < 0)
{
perror(myFifo.getName());
return 1;
}
if ((returnedValue = open(serverFifo.getName(), O_WRONLY)) == -1)
{
perror(serverFifo.getName());
return 1;
}
serverFifo.pipeDescriptor = returnedValue;
while(true)
{
cout << "\ncmd" <<endl;
#ifdef DEBUG
cout <<"PIPE of server: " << serverFifo.getName() <<endl;
#endif
numberOfBytes = read(fileno(stdin), buffer.message, PIPE_BUF);
#ifdef DEBUG
cout <<"what you write: "<<buffer.message<<endl;
#endif
if (!strncmp("exit", buffer.message, numberOfBytes - 1))
break;
write(serverFifo.pipeDescriptor,(char *) &buffer, sizeof(buffer));
returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);
if(returnedValue == -1)
{
perror(myFifo.getName());
return 1;
}
myFifo.pipeDescriptor = returnedValue;
readOK = 0;
while (readOK != 1)
{
if((numberOfBytes = read(myFifo.pipeDescriptor, buffer.message, PIPE_BUF)) > 0)
{
write(fileno(stdout), buffer.message, numberOfBytes);
memset(buffer.message,0x0,PIPE_BUF);
readOK=1;
}
}
myFifo.closePipe();
}
serverFifo.closePipe();
return 1;
}
Agora a implementação de nossa classe
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <errno.h>
using namespace std;
typedef struct dataSet
{
char name[255];
char path[255];
char message[PIPE_BUF];
}data;
class FIFO
{
private:
data info;
public:
int pipeDescriptor;
char* getName();
void setName(char*);
char* getPath();
void setPath(char*);
char* getMessage();
void writeMessage(char*);
void closePipe();
int writeToPipe(FIFO&);
int readFromPipe(FIFO&);
};
char* FIFO::getName()
{
return info.name;
}
void FIFO::setName(char * nameToSet)
{
strcpy(info.name,nameToSet);
}
char* FIFO::getPath()
{
return info.path;
}
void FIFO::setPath(char *pathToSet)
{
strcpy(info.path, pathToSet);
}
char* FIFO::getMessage()
{
return info.message;
}
void FIFO::writeMessage(char *messageToWrite)
{
strcpy(info.message, messageToWrite);
}
int FIFO::writeToPipe(FIFO& pipeToWrite)
{
FILE * result;
int readFromFile, numberOfBytes, popenIsOk;
char buffer[PIPE_BUF], stringBuff[PIPE_BUF];
#ifdef DEBUG
cout <<"what will be the command to sheel: " << getMessage()<<endl;
#endif
result = popen(getMessage(), "r");
if(!result)
{
#ifdef DEBUG
cout <<"result is null" << endl;
perror("errot in open file");
#endif
strcpy(stringBuff,"Command not found");
write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
return -1;
}
popenIsOk=0;
while(popenIsOk!=1)
{
if ((numberOfBytes = read(fileno(result), buffer, PIPE_BUF))>0)
{
strcpy(stringBuff,buffer);
write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
memset(buffer, 0x0, PIPE_BUF);
#ifdef DEBUG
cout <<"what will be written to client: " << stringBuff << endl;
#endif
popenIsOk=1;
}
}
pclose(result);
}
int FIFO::readFromPipe(FIFO& pipe)
{
int returnedValue;
data buffer;
returnedValue = read(pipeDescriptor, (char *) &buffer, sizeof(buffer));
if(returnedValue <=0 )
return returnedValue;
else
{
writeMessage(buffer.message);
pipe.setName(buffer.name);
}
return returnedValue;
}
void FIFO::closePipe()
{
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:
Comando: make all
Para executar:
Comando para Rodar o Server: ./PipeServer FIFO /home/eduardo/Documents/programas/FIFO
Onde:
- FIFO é o Nome da PIPE a ser Criada
- /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela vai ser criada
Comando para Rodar o cliente: ./client FIFO /home/eduardo/Documents/programas/FIFO
- FIFO é o Nome da PIPE criada pelo server
- /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela foi criada
Como o Server roda em BackGround, se você desejar para o server dê o comando: ./PipeServer Stop
Para 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): ./PipeServer Debug /home/eduardo/Documents/programas/FIFO
Para rodar o cliente: ./client Debug /home/eduardo/Documents/programas/FIFO
Bons Testes!!!!