Implementando pipes de entrada no cliente
Ao usar um pipe de entrada para transferir dados do cliente para o servidor, você deve implementar um procedimento de pull. O procedimento de pull deve localizar os dados a serem transferidos, ler os dados no buffer e definir o número de elementos a serem enviados. Nem todos os dados precisam estar no buffer quando o servidor começa a efetuar pull de dados para si mesmo. O procedimento de pull pode preencher o buffer incrementalmente.
Quando não há mais dados a serem enviados, o procedimento define seu último argumento como zero. Quando todos os dados são enviados, o procedimento de pull deve fazer qualquer limpeza necessária antes de retornar. Para um parâmetro que é um pipe [in, out], o procedimento de pull deve redefinir a variável de estado do cliente depois que todos os dados tiverem sido transmitidos, para que o procedimento de push possa usá-lo para receber dados.
O exemplo a seguir é extraído do programa Pipedemo incluído no SDK (Platform Software Development Kit).
//file: client.c (fragment)
#include <windows.h>
#include "pipedemo.h"
long *globalPipeData;
long globalBuffer[BUF_SIZE];
ulong pipeDataIndex; /* state variable */
void SendLongs()
{
LONG_PIPE inPipe;
int i;
globalPipeData =
(long *)malloc( sizeof(long) * PIPE_SIZE );
for (i=0; i<PIPE_SIZE; i++)
globalPipeData[i] = IN_VALUE;
pipeDataIndex = 0;
inPipe.state = (rpc_ss_pipe_state_t )&pipeDataIndex;
inPipe.pull = PipePull;
inPipe.alloc = PipeAlloc;
InPipe( inPipe ); /* Make the rpc */
free( (void *)globalPipeData );
}//end SendLongs
void PipeAlloc( rpc_ss_pipe_state_t stateInfo,
ulong requestedSize,
long **allocatedBuffer,
ulong *allocatedSize )
{
ulong *state = (ulong *)stateInfo;
if ( requestedSize > (BUF_SIZE*sizeof(long)) )
{
*allocatedSize = BUF_SIZE * sizeof(long);
}
else
{
*allocatedSize = requestedSize;
}
*allocatedBuffer = globalBuffer;
} //end PipeAlloc
void PipePull( rpc_ss_pipe_state_t stateInfo,
long *inputBuffer,
ulong maxBufSize,
ulong *sizeToSend )
{
ulong currentIndex;
ulong i;
ulong elementsToRead;
ulong *state = (ulong *)stateInfo;
currentIndex = *state;
if (*state >= PIPE_SIZE )
{
*sizeToSend = 0; /* end of pipe data */
*state = 0; /* Reset the state = global index */
}
else
{
if ( currentIndex + maxBufSize > PIPE_SIZE )
elementsToRead = PIPE_SIZE - currentIndex;
else
elementsToRead = maxBufSize;
for (i=0; i < elementsToRead; i++)
{
/*client sends data */
inputBuffer[i] = globalPipeData[i + currentIndex];
}
*state += elementsToRead;
*sizeToSend = elementsToRead;
}
}//end PipePull
Este exemplo inclui o arquivo de cabeçalho gerado pelo compilador MIDL. Para obter detalhes, consulte Definindo pipes em arquivos IDL. Ele também declara uma variável que usa como a fonte de dados chamada globalPipeData. A variável globalBuffer é um buffer que o procedimento de pull usa para enviar os blocos de dados obtidos de globalPipeData.
A função SendLongs declara o pipe de entrada e aloca memória para a variável de fonte de dados globalPipeData. No programa cliente/servidor, a fonte de dados pode ser um arquivo ou estrutura que o cliente cria. Você também pode fazer com que o programa cliente obtenha dados do servidor, processe-os e retorne-os ao servidor usando um pipe de entrada. Neste exemplo simples, a fonte de dados é um buffer alocado dinamicamente de inteiros longos.
Antes que a transferência possa começar, o cliente deve definir ponteiros para a variável de estado, o procedimento de pull e o procedimento de alocação. Esses ponteiros são mantidos na variável de pipe que o cliente declara. Nesse caso, SendLongs declara inPipe. Você pode usar qualquer tipo de dados apropriado para sua variável de estado.
Os clientes iniciam transferências de dados em um pipe invocando um procedimento remoto no servidor. Chamar o procedimento remoto informa ao programa de servidor que o cliente está pronto para transmitir. Em seguida, o servidor pode efetuar pull dos dados para si mesmo. Este exemplo invoca um procedimento remoto chamado InPipe. Depois que os dados são transferidos para o servidor, a função SendLongs libera o buffer alocado dinamicamente.
Em vez de alocar memória sempre que um buffer for necessário. o procedimento alloc neste exemplo simplesmente define um ponteiro para a variável globalBuffer. O procedimento de pull reutiliza esse buffer sempre que transfere dados. Programas cliente mais complexos podem precisar alocar um novo buffer sempre que o servidor efetuar pull de dados do cliente.
O stub do cliente chama o procedimento de pull. O procedimento de pull neste exemplo usa a variável de estado para acompanhar a próxima posição no buffer de fonte de dados global do qual ler. Ele lê dados do buffer de origem no buffer de pipe. O stub do cliente transmite os dados para o servidor. Quando todos os dados tiverem sido enviados, o procedimento de pull definirá o tamanho do buffer como zero. Isso instrui o servidor a parar de efetuar pull de dados.
Tópicos relacionados