Compartilhar via


Implementando pipes de saída no cliente

Ao usar um pipe de saída para transferir dados do servidor para o cliente, você deve implementar um procedimento push em seu cliente. O procedimento push leva um ponteiro para um buffer e uma contagem de elementos do stub do cliente e, se a contagem de elementos for maior que 0, processará os dados. Por exemplo, ele pode copiar os dados do buffer do stub para sua própria memória. Como alternativa, ele pode processar os dados no buffer do stub e salvá-los em um arquivo. Quando a contagem de elementos é igual a zero, o procedimento de push conclui todas as tarefas de limpeza necessárias antes de retornar.

No exemplo a seguir, a função cliente ReceiveLongs aloca uma estrutura de pipe e um buffer de memória global. Ele inicializa a estrutura, faz a chamada de procedimento remoto e libera a memória.

Exemplo

//file: client.c (fragment)
#include <windows.h>
#include "pipedemo.h"
long *  globalPipeData;
long    globalBuffer[BUF_SIZE];
 
ulong   pipeDataIndex; /* state variable */
 
void ReceiveLongs()
{
    LONG_PIPE *outputPipe;
    idl_long_int i;
 
    globalPipeData =
        (long *)malloc( sizeof(long) * PIPE_SIZE );
    
    pipeDataIndex = 0;
    outputPipe.state =  (rpc_ss_pipe_state_t )&pipeDataIndex;
    outputPipe.push  = PipePush;
    outputPipe.alloc = PipeAlloc;
 
    OutPipe( &outputPipe ); /* Make the rpc */
 
    free( (void *)globalPipeData );
 
}//end ReceiveLongs()

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 PipePush( rpc_ss_pipe_state_t stateInfo,
               long *buffer,
               ulong numberOfElements )
{
    ulong elementsToCopy, i;
    ulong *state = (ulong *)stateInfo;

    if (numberOfElements == 0)/* end of data */
    {
        *state = 0; /* Reset the state = global index */
    }
    else
    {
        if (*state + numberOfElements > PIPE_SIZE)
            elementsToCopy = PIPE_SIZE - *state;
        else
            elementsToCopy = numberOfElements;
 
        for (i=0; i <elementsToCopy; i++)
        { 
            /*client receives data */
            globalPipeData[*state] = buffer[i];
            (*state)++;
        }
    }
}//end PipePush

Este exemplo inclui o arquivo de cabeçalho gerado pelo compilador MIDL. Para obter detalhes, consulte Definindo pipes no arquivo IDL. Ele também declara uma variável, globalPipeData, que usa como coletor de dados. A variável globalBuffer é um buffer que o procedimento de push usa para receber blocos de dados armazenados no globalPipeData.

A função ReceiveLongs declara um pipe e aloca espaço de memória para a variável de coletor de dados global. No programa cliente/servidor, o coletor de dados pode ser um arquivo ou estrutura de dados que o cliente cria. Neste exemplo simples, a fonte de dados é um buffer alocado dinamicamente de inteiros longos.

Antes que a transferência de dados possa começar, seu programa cliente deve inicializar a estrutura do pipe de saída. Ele deve definir ponteiros para a variável de estado, o procedimento push e o procedimento alloc. Neste exemplo, a variável de pipe de saída é chamada outputPipe.

Os clientes sinalizam aos servidores que estão prontos para receber dados invocando um procedimento remoto no servidor. Neste exemplo, o procedimento remoto é chamado OutPipe. Quando o cliente chama o procedimento remoto, o servidor inicia a transferência de dados. Sempre que os dados chegam, o stub do cliente chama os procedimentos de aloc e push do cliente conforme necessário.

Em vez de alocar memória sempre que um buffer é necessário, o procedimento alloc neste exemplo simplesmente define um ponteiro para a variável globalBuffer. Em seguida, o procedimento de pull reutiliza esse buffer sempre que transfere dados. Programas de cliente mais complexos podem precisar alocar um novo buffer sempre que o servidor efetuar pull de dados do cliente.

O procedimento push neste exemplo usa a variável de estado para acompanhar a próxima posição em que armazenará dados no buffer do coletor de dados global. Ele grava dados do buffer de pipe no buffer do coletor. Em seguida, o stub do cliente recebe o próximo bloco de dados do servidor e os armazena no buffer de pipe. Quando todos os dados são enviados, o servidor transmite um buffer de tamanho zero. Isso indica o procedimento push para parar de receber dados.

Tubo

/Oi