Server per named pipe con operazioni di I/O sovrapposte
Di seguito è riportato un esempio di server pipe a thread singolo che usa operazioni sovrapposte per gestire connessioni simultanee a più client pipe. Il server pipe crea un numero fisso di istanze pipe. Ogni istanza della pipe può essere collegata a un client pipe separato. Al termine dell'uso dell'istanza della pipe da parte di un client pipe, il server si disconnette dal client e riutilizza l'istanza della pipe per connettersi a un nuovo client. Questo server pipe può essere usato con il client di pipe descritto in client named pipe.
La struttura diOVERLAPPEDviene specificata come parametro in ogni ReadFile, WriteFilee 'operazione di ConnectNamedPipe sull'istanza della pipe. Sebbene l'esempio mostri operazioni simultanee su istanze di pipe diverse, evita operazioni simultanee su una singola istanza di pipe usando l'oggetto evento nella struttura OVERLAPPED. Poiché lo stesso oggetto evento viene usato per le operazioni di lettura, scrittura e connessione per ogni istanza, non è possibile sapere quale completamento dell'operazione ha causato l'impostazione dell'evento sullo stato segnalato per le operazioni simultanee che usano la stessa istanza della pipe.
Gli handle di evento per ogni istanza della pipe vengono archiviati in una matrice che viene passata alla funzione WaitForMultipleObjects. Questa funzione attende che uno degli eventi venga segnalato e restituisce l'indice di matrice dell'evento che ha causato il completamento dell'operazione di attesa. Nell'esempio di questo argomento viene utilizzato questo indice dell'array per recuperare una struttura contenente informazioni sull'istanza della pipe. Il server utilizza il membro fPendingIO della struttura per tenere traccia se l'operazione di I/O più recente sull'istanza fosse in sospeso, il che richiede una chiamata alla funzione GetOverlappedResult. Il server utilizza il membro dwState della struttura per determinare la prossima operazione da eseguire per l'istanza della pipe.
Le operazioni sovrapposte ReadFile, WriteFilee ConnectNamedPipe possono essere completate quando la funzione restituisce. In caso contrario, se l'operazione è in sospeso, l'oggetto evento nella struttura OVERLAPPED specificata viene impostato sullo stato non segnalato prima che la funzione restituisca. Al termine dell'operazione in sospeso, il sistema imposta lo stato dell'oggetto evento su segnalato. Lo stato dell'oggetto evento non viene modificato se l'operazione termina prima della restituzione della funzione.
Poiché nell'esempio vengono utilizzati oggetti evento di reimpostazione manuale, lo stato di un oggetto evento non viene cambiato in non segnalato dalla funzione WaitForMultipleObjects. Questo aspetto è importante, perché l'esempio si basa sugli oggetti evento rimanenti nello stato segnalato, tranne quando è presente un'operazione in sospeso.
Se l'operazione è già stata completata quando ReadFile, WriteFileo ConnectNamedPipe restituisce, il valore restituito della funzione indica il risultato. Per le operazioni di lettura e scrittura, viene restituito anche il numero di byte trasferiti. Se l'operazione è ancora in sospeso, la funzione ReadFile, WriteFileo ConnectNamedPipe restituisce zero e la funzione GetLastError restituisce ERROR_IO_PENDING. In questo caso, usare la funzionegetOverlappedResultper recuperare i risultati al termine dell'operazione. GetOverlappedResult restituisce solo i risultati delle operazioni in sospeso. Non segnala i risultati delle operazioni completate prima che la funzione sovrapposta ReadFile, WriteFileo ConnectNamedPipe abbia restituito.
Prima di disconnettersi da un client, è necessario attendere un segnale che indica che il client è terminato. Lo svuotamento dei buffer di file vanifica lo scopo dell'I/O sovrapposto, perché l'operazione di svuotamento blocca l'esecuzione del thread del server mentre attende che il client svuoti la pipe. In questo esempio, il segnale è l'errore generato tentando di leggere dalla pipe dopo che il client della pipe chiude il relativo handle.
#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);
}
Argomenti correlati