クライアントでの出力パイプの実装
出力パイプを使用してサーバーからクライアントにデータを転送する場合は、クライアントにプッシュ プロシージャを実装する必要があります。 プッシュ プロシージャは、クライアント スタブからバッファーと要素数へのポインターを受け取り、要素数が 0 より大きい場合はデータを処理します。 たとえば、スタブのバッファーから独自のメモリにデータをコピーできます。 または、スタブのバッファー内のデータを処理し、ファイルに保存することもできます。 要素数が 0 の場合、プッシュ プロシージャは、戻る前に必要なクリーンアップ タスクを完了します。
次の例では、クライアント関数 ReceiveLongs によってパイプ構造とグローバル メモリ バッファーが割り当てられます。 構造体を初期化し、リモート プロシージャ呼び出しを行い、メモリを解放します。
例
//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
この例には、MIDL コンパイラによって生成されたヘッダー ファイルが含まれています。 詳細については、「 IDL ファイルでのパイプの定義」を参照してください。 また、データ シンクとして使用する変数 globalPipeData も宣言します。 変数 globalBuffer は、プッシュ プロシージャが globalPipeData に格納するデータブロックを受信するために使用するバッファーです。
ReceiveLongs 関数はパイプを宣言し、グローバル データ シンク変数のメモリ領域を割り当てます。 クライアント/サーバー プログラムでは、データ シンクは、クライアントが作成するファイルまたはデータ構造にすることができます。 この簡単な例では、データ ソースは長整数の動的に割り当てられたバッファーです。
データ転送を開始するには、クライアント・プログラムが出力パイプ構造を初期化する必要があります。 状態変数、プッシュ プロシージャ、およびアロケーション プロシージャへのポインターを設定する必要があります。 この例では、出力パイプ変数を outputPipe と呼びます。
クライアントは、サーバーでリモート プロシージャを呼び出すことによって、データを受信する準備ができていることをサーバーに通知します。 この例では、リモート プロシージャは OutPipe と呼ばれます。 クライアントがリモート プロシージャを呼び出すと、サーバーはデータ転送を開始します。 データが到着するたびに、クライアント スタブは必要に応じてクライアントのアロケーションプロシージャとプッシュプロシージャを呼び出します。
この例のアロケーション プロシージャは、バッファーが必要になるたびにメモリを割り当てるのではなく、変数 globalBuffer へのポインターを設定するだけです。 その後、プル プロシージャは、データを転送するたびにこのバッファーを再利用します。 より複雑なクライアント プログラムでは、サーバーがクライアントからデータをプルするたびに新しいバッファーを割り当てる必要がある場合があります。
この例のプッシュ プロシージャでは、状態変数を使用して、グローバル データ シンク バッファーにデータを格納する次の位置を追跡します。 パイプ バッファーからシンク バッファーにデータを書き込みます。 次に、クライアント スタブはサーバーから次のデータ ブロックを受信し、パイプ バッファーに格納します。 すべてのデータが送信されると、サーバーは 0 サイズのバッファーを送信します。 これにより、プッシュ プロシージャがデータの受信を停止します。
関連トピック