Named Pipe-Server mit überlappenden E/A-Vorgängen
Im Folgenden finden Sie ein Beispiel für einen Singlethread-Pipeserver, der überlappende Vorgänge verwendet, um gleichzeitige Verbindungen mit mehreren Pipeclients zu verarbeiten. Der Pipeserver erstellt eine feste Anzahl von Pipeinstanzen. Jede Pipe instance kann mit einem separaten Pipeclient verbunden werden. Wenn ein Pipeclient die Verwendung seiner Pipe-instance abgeschlossen hat, trennt der Server die Verbindung mit dem Client und verwendet die Pipe instance wieder, um eine Verbindung mit einem neuen Client herzustellen. Dieser Pipeserver kann mit dem unter Named Pipe-Client beschriebenen Pipeclient verwendet werden.
Die OVERLAPPED-Struktur wird als Parameter in jedem ReadFile-, WriteFile- und ConnectNamedPipe-Vorgang auf der Pipe instance angegeben. Obwohl das Beispiel gleichzeitige Vorgänge für verschiedene Pipeinstanzen zeigt, werden gleichzeitige Vorgänge für eine einzelne Pipe instance vermieden, indem das Ereignisobjekt in der OVERLAPPED-Struktur verwendet wird. Da dasselbe Ereignisobjekt für Lese-, Schreib- und Verbindungsvorgänge für jeden instance verwendet wird, gibt es keine Möglichkeit, zu wissen, welcher Vorgang abgeschlossen hat, dass das Ereignis für gleichzeitige Vorgänge mit demselben Pipe-instance auf den signalierten Zustand festgelegt wurde.
Die Ereignishandles für jede Pipe instance in einem Array gespeichert werden, das an die WaitForMultipleObjects-Funktion übergeben wird. Diese Funktion wartet, bis eines der Ereignisse signalisiert wird, und gibt den Arrayindex des Ereignisses zurück, durch das der Wartevorgang abgeschlossen wurde. Im Beispiel in diesem Thema wird dieser Arrayindex verwendet, um eine Struktur abzurufen, die Informationen für die Pipe instance. Der Server verwendet den fPendingIO-Member der -Struktur, um nachzuverfolgen, ob der letzte E/A-Vorgang auf der instance ausstehend war. Dies erfordert einen Aufruf der GetOverlappedResult-Funktion. Der Server verwendet den dwState-Member der -Struktur, um den nächsten Vorgang zu bestimmen, der für die Pipe instance ausgeführt werden muss.
Überlappende ReadFile-, WriteFile- und ConnectNamedPipe-Vorgänge können zum Zeitpunkt der Rückgabe der Funktion abgeschlossen werden. Andernfalls wird das Ereignisobjekt in der angegebenen OVERLAPPED-Struktur auf den nicht signalierten Zustand festgelegt, wenn der Vorgang aussteht, bevor die Funktion zurückgibt. Wenn der ausstehende Vorgang abgeschlossen ist, legt das System den Status des Ereignisobjekts auf signalisiert fest. Der Zustand des Ereignisobjekts wird nicht geändert, wenn der Vorgang abgeschlossen wird, bevor die Funktion zurückgegeben wird.
Da im Beispiel Ereignisobjekte zum manuellen Zurücksetzen verwendet werden, wird der Zustand eines Ereignisobjekts von der WaitForMultipleObjects-Funktion nicht in nicht signalisiert geändert. Dies ist wichtig, da das Beispiel darauf basiert, dass die Ereignisobjekte im signalierten Zustand verbleiben, es sei denn, es liegt ein ausstehender Vorgang vor.
Wenn der Vorgang bereits abgeschlossen ist, wenn ReadFile, WriteFile oder ConnectNamedPipe zurückgegeben wird, gibt der Rückgabewert der Funktion das Ergebnis an. Bei Lese- und Schreibvorgängen wird auch die Anzahl der übertragenen Bytes zurückgegeben. Wenn der Vorgang noch aussteht, gibt die ReadFile-, WriteFile- oder ConnectNamedPipe-Funktion null zurück, und die GetLastError-Funktion gibt ERROR_IO_PENDING zurück. Verwenden Sie in diesem Fall die GetOverlappedResult-Funktion , um die Ergebnisse nach Abschluss des Vorgangs abzurufen. GetOverlappedResult gibt nur die Ergebnisse ausstehender Vorgänge zurück. Die Ergebnisse von Vorgängen, die abgeschlossen wurden, bevor die überlappende ReadFile-, WriteFile- oder ConnectNamedPipe-Funktion zurückgegeben wurde, werden nicht gemeldet.
Bevor Sie die Verbindung mit einem Client trennen, müssen Sie auf ein Signal warten, das angibt, dass der Client abgeschlossen ist. (Das Leeren der Dateipuffer würde den Zweck überlappender E/A-Vorgänge verleeren, da der Leerungsvorgang die Ausführung des Serverthreads blockieren würde, während er darauf wartet, dass der Client die Pipe leert.) In diesem Beispiel ist das Signal der Fehler, der generiert wird, wenn versucht wird, aus der Pipe zu lesen, nachdem der Pipeclient seinen Handle geschlossen hat.
#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);
}
Zugehörige Themen