Partilhar via


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 pull. O procedimento 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 extrair dados para si mesmo. O procedimento pull pode preencher o buffer incrementalmente.

Quando não há mais dados para enviar, 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 é uma canalização [in, out], o procedimento pull deve redefinir a variável de estado do cliente após a transmissão de todos os dados, para que o procedimento push possa utilizá-la para receber dados.

O exemplo a seguir é extraído do programa Pipedemo incluído com o Platform Software Development Kit (SDK).

//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 fonte de dados chamada globalPipeData. A variável globalBuffer é um buffer que o procedimento pull usa para enviar os blocos de dados que obtém de globalPipeData.

A função SendLongs declara o pipe de entrada e aloca memória para a variável de fonte de dados globalPipeData. Em seu programa cliente/servidor, a fonte de dados pode ser um arquivo ou estrutura que o cliente cria. Você também pode fazer com que seu programa cliente obtenha dados do servidor, processe-os e devolva-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 pull e o procedimento alloc. Esses ponteiros são mantidos na variável de tubo declarada pelo cliente. Neste 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 através de um pipe invocando um procedimento remoto no servidor. Chamar o procedimento remoto informa ao programa do servidor que o cliente está pronto para transmitir. O servidor pode então extrair os 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 cada vez que um buffer é necessário. o procedimento alloc neste exemplo simplesmente define um ponteiro para a variável globalBuffer. O procedimento pull reutiliza esse buffer cada vez que transfere dados. Programas cliente mais complexos podem precisar alocar um novo buffer cada vez que o servidor extrai dados do cliente.

O stub do cliente chama o procedimento de extração. O procedimento pull neste exemplo usa a variável state para controlar a próxima posição no buffer da fonte de dados global a ser lida. Ele lê dados do buffer de origem para o buffer de tubo. O stub do cliente transmite os dados para o servidor. Quando todos os dados tiverem sido enviados, o procedimento pull define o tamanho do buffer como zero. Isso diz ao servidor para parar de extrair dados.

tubo

/Oi