在客户端上实现输入管道

使用输入管道将数据从客户端传输到服务器时,必须实现拉取过程。 拉取过程必须找到要传输的数据,将数据读入缓冲区,并设置要发送的元素数。 当服务器开始向自身拉取数据时,并非所有数据都必须在缓冲区中。 拉取过程可以增量填充缓冲区。

如果没有更多要发送的数据,该过程将其最后一个参数设置为零。 发送所有数据后,拉取过程应在返回之前执行任何所需的清理。 对于作为 [in, out] 管道的参数,拉取过程必须在传输所有数据后重置客户端的状态变量,以便推送过程可以使用它来接收数据。

以下示例是从平台软件开发工具包 (SDK) 附带的 Pipedemo 程序中提取的。

//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

此示例包含由 MIDL 编译器生成的头文件。 有关详细信息,请参阅 在 IDL 文件中定义管道。 它还声明一个变量,该变量用作名为 globalPipeData 的数据源。 变量 globalBuffer 是拉取过程用来发送从 globalPipeData 获取的数据块的缓冲区。

SendLongs 函数声明输入管道,并为数据源变量 globalPipeData 分配内存。 在客户端/服务器程序中,数据源可以是客户端创建的文件或结构。 还可以让客户端程序从服务器获取数据、对其进行处理,并使用输入管道将其返回到服务器。 在此简单示例中,数据源是动态分配的长整数缓冲区。

在传输开始之前,客户端必须设置指向状态变量、拉取过程和分配过程的指针。 这些指针保留在客户端声明的管道变量中。 在本例中,SendLongs 声明 inPipe。 可以为状态变量使用任何适当的数据类型。

客户端通过在服务器上调用远程过程来启动跨管道的数据传输。 调用远程过程会告知服务器程序客户端已准备好传输。 然后,服务器可以将数据拉取到自身。 此示例调用名为 InPipe 的远程过程。 将数据传输到服务器后,SendLongs 函数释放动态分配的缓冲区。

而不是每次需要缓冲区时分配内存。 此示例中的分配过程只是设置指向变量 globalBuffer 的指针。 每次传输数据时,拉取过程都会重复使用此缓冲区。 在服务器每次从客户端拉取数据时,更复杂的客户端程序可能需要分配新的缓冲区。

客户端存根调用拉取过程。 此示例中的拉取过程使用 state 变量跟踪要从中读取的全局数据源缓冲区中的下一个位置。 它将源缓冲区中的数据读取到管道缓冲区。 客户端存根将数据传输到服务器。 发送所有数据后,拉取过程会将缓冲区大小设置为零。 这会告知服务器停止拉取数据。

/Oi