重複した I/O を使用する名前付きパイプ サーバー
重複した操作を使用して複数のパイプ クライアントへの同時接続を処理するシングル スレッド パイプ サーバーの例を次に示します。 パイプ サーバーは、固定数のパイプ インスタンスを作成します。 各パイプ インスタンスは、個別のパイプ クライアントに接続できます。 パイプ クライアントがパイプ インスタンスの使用を完了すると、サーバーはクライアントから切断され、パイプ インスタンスを再利用して新しいクライアントに接続します。 このパイプ サーバーは、「 名前付きパイプ クライアント」で説明されているパイプ クライアントと共に使用できます。
OVERLAPPED 構造体は、パイプ インスタンスに対する ReadFile、WriteFile、ConnectNamedPipe の各操作でパラメーターとして指定されます。 この例では、異なるパイプ インスタンスに対する同時操作を示していますが、 OVERLAPPED 構造体のイベント オブジェクトを使用することで、単一のパイプ インスタンスに対する同時操作を回避します。 各インスタンスの読み取り、書き込み、接続操作には同じイベント オブジェクトが使用されるため、同じパイプ インスタンスを使用する同時操作に対してイベントがシグナル状態に設定された原因となった操作を知る方法はありません。
各パイプ インスタンスのイベント ハンドルは、 WaitForMultipleObjects 関数に渡される配列に格納されます。 この関数は、いずれかのイベントが通知されるのを待機し、待機操作が完了した原因となったイベントの配列インデックスを返します。 このトピックの例では、この配列インデックスを使用して、パイプ インスタンスの情報を含む構造体を取得します。 サーバーは 構造体の fPendingIO メンバーを使用して、インスタンスに対する最新の I/O 操作が保留中であったかどうかを追跡します。これには 、GetOverlappedResult 関数の呼び出しが必要です。 サーバーは 構造体の dwState メンバーを使用して、パイプ インスタンスに対して実行する必要がある次の操作を決定します。
重複する ReadFile、 WriteFile、 および ConnectNamedPipe 操作は、関数が返すまでに完了できます。 それ以外の場合、操作が保留中の場合、指定した OVERLAPPED 構造体のイベント オブジェクトは、関数が返される前に、非署名状態に設定されます。 保留中の操作が完了すると、システムはイベント オブジェクトの状態を signaled に設定します。 関数が戻る前に操作が終了した場合、イベント オブジェクトの状態は変更されません。
この例では手動リセット イベント オブジェクトを使用しているため、イベント オブジェクトの状態は WaitForMultipleObjects 関数によって非署名に変更されません。 この例では、保留中の操作がある場合を除き、シグナル状態の残りのイベント オブジェクトに依存しているため、これは重要です。
ReadFile、WriteFile、または ConnectNamedPipe が返されたときに操作が既に完了している場合、関数の戻り値は結果を示します。 読み取り操作と書き込み操作では、転送されたバイト数も返されます。 操作がまだ保留中の場合、 ReadFile、 WriteFile、または ConnectNamedPipe 関数は 0 を返し、 GetLastError 関数はERROR_IO_PENDINGを返します。 この場合は、 GetOverlappedResult 関数を使用して、操作の完了後に結果を取得します。 GetOverlappedResult は、保留中の操作の結果のみを返します。 重複した ReadFile、 WriteFile、または ConnectNamedPipe 関数が返される前に完了した操作の結果は報告されません。
クライアントから切断する前に、クライアントが完了したことを示すシグナルを待機する必要があります。 (ファイル バッファーをフラッシュすると、クライアントがパイプを空になるのを待機している間に、フラッシュ操作によってサーバー スレッドの実行がブロックされるため、重複する I/O の目的が妨げられます)。この例では、シグナルは、パイプ クライアントがハンドルを閉じた後にパイプから読み取ろうとすることによって生成されるエラーです。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;
VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
int _tmain(VOID)
{
DWORD i, dwWait, cbRet, dwErr;
BOOL fSuccess;
LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
// The initial loop creates several instances of a named pipe
// along with an event object for each instance. An
// overlapped ConnectNamedPipe operation is started for
// each instance.
for (i = 0; i < INSTANCES; i++)
{
// Create an event object for this instance.
hEvents[i] = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
Pipe[i].oOverlap.Offset = 0;
Pipe[i].oOverlap.OffsetHigh = 0;
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
INSTANCES, // number of instances
BUFSIZE*sizeof(TCHAR), // output buffer size
BUFSIZE*sizeof(TCHAR), // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // default security attributes
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
// Call the subroutine to connect to the new client
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
while (1)
{
// Wait for the event object to be signaled, indicating
// completion of an overlapped read, write, or
// connect operation.
dwWait = WaitForMultipleObjects(
INSTANCES, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE); // waits indefinitely
// dwWait shows which pipe completed the operation.
i = dwWait - WAIT_OBJECT_0; // determines which pipe
if (i < 0 || i > (INSTANCES - 1))
{
printf("Index out of range.\n");
return 0;
}
// Get the result if the operation was pending.
if (Pipe[i].fPendingIO)
{
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, // handle to pipe
&Pipe[i].oOverlap, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // do not wait
switch (Pipe[i].dwState)
{
// Pending connect operation
case CONNECTING_STATE:
if (! fSuccess)
{
printf("Error %d.\n", GetLastError());
return 0;
}
Pipe[i].dwState = READING_STATE;
break;
// Pending read operation
case READING_STATE:
if (! fSuccess || cbRet == 0)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].cbRead = cbRet;
Pipe[i].dwState = WRITING_STATE;
break;
// Pending write operation
case WRITING_STATE:
if (! fSuccess || cbRet != Pipe[i].cbToWrite)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
// The pipe state determines which operation to do next.
switch (Pipe[i].dwState)
{
// READING_STATE:
// The pipe instance is connected to the client
// and is ready to read a request from the client.
case READING_STATE:
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chRequest,
BUFSIZE*sizeof(TCHAR),
&Pipe[i].cbRead,
&Pipe[i].oOverlap);
// The read operation completed successfully.
if (fSuccess && Pipe[i].cbRead != 0)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = WRITING_STATE;
continue;
}
// The read operation is still pending.
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
// An error occurred; disconnect from the client.
DisconnectAndReconnect(i);
break;
// WRITING_STATE:
// The request was successfully read from the client.
// Get the reply data and write it to the client.
case WRITING_STATE:
GetAnswerToRequest(&Pipe[i]);
fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chReply,
Pipe[i].cbToWrite,
&cbRet,
&Pipe[i].oOverlap);
// The write operation completed successfully.
if (fSuccess && cbRet == Pipe[i].cbToWrite)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
continue;
}
// The write operation is still pending.
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
Pipe[i].fPendingIO = TRUE;
continue;
}
// An error occurred; disconnect from the client.
DisconnectAndReconnect(i);
break;
default:
{
printf("Invalid pipe state.\n");
return 0;
}
}
}
return 0;
}
// DisconnectAndReconnect(DWORD)
// This function is called when an error occurs or when the client
// closes its handle to the pipe. Disconnect from this client, then
// call ConnectNamedPipe to wait for another client to connect.
VOID DisconnectAndReconnect(DWORD i)
{
// Disconnect the pipe instance.
if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
// Call a subroutine to connect to the new client.
Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);
Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : // still connecting
READING_STATE; // ready to read
}
// ConnectToNewClient(HANDLE, LPOVERLAPPED)
// This function is called to start an overlapped connect operation.
// It returns TRUE if an operation is pending or FALSE if the
// connection has been completed.
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
// Start an overlapped connection for this pipe instance.
fConnected = ConnectNamedPipe(hPipe, lpo);
// Overlapped ConnectNamedPipe should return zero.
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
// Client is already connected, so signal an event.
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
// If an error occurs during the connect operation...
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
VOID GetAnswerToRequest(LPPIPEINST pipe)
{
_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}
関連トピック