Namngiven Pipe-server med överlappande I/O
Följande är ett exempel på en enda trådad rörserver som använder överlappande åtgärder för att betjäna samtidiga anslutningar till flera pipe-klienter. Rörservern skapar ett fast antal pipe-instanser. Varje pipe-instans kan anslutas till en separat pipe-klient. När en pipe-klient har avslutat användningen av sin pipe-instans kopplar servern bort klienten och återanvänder servern pipe-instansen för att ansluta till en ny klient. Den här pipe-servern kan användas med pipe-klienten som beskrivs i Named Pipe Client.
Strukturen OVERLAPPED anges som en parameter i varje ReadFile, WriteFileoch ConnectNamedPipe på pipe-instansen. Även om exemplet visar samtidiga åtgärder på olika pipe-instanser undviker det samtidiga åtgärder på en enda pipe-instans med hjälp av händelseobjektet i OVERLAPPED- struktur. Eftersom samma händelseobjekt används för läs-, skriv- och anslutningsåtgärder för varje instans, finns det inget sätt att veta vilken åtgärd som slutfördes som gjorde att händelsen angavs till signalerat tillstånd för samtidiga åtgärder med samma pipe-instans.
Händelsereferenserna för varje pipe-instans lagras i en matris som skickas till funktionen WaitForMultipleObjects. Den här funktionen väntar på att en av händelserna ska signaleras och returnerar matrisindexet för händelsen som gjorde att vänteåtgärden slutfördes. Exemplet i det här avsnittet använder det här matrisindexet för att hämta en struktur som innehåller information för pipe-instansen. Servern använder medlemsvariabeln fPendingIO- i strukturen för att hålla reda på om den senaste I/O-åtgärden på instansen var pågående, vilket kräver ett anrop till GetOverlappedResult. Servern använder dwState medlem i strukturen för att fastställa nästa åtgärd som måste utföras för pipe-instansen.
Överlappade ReadFile, WriteFileoch ConnectNamedPipe åtgärder kan slutföras när funktionen returneras. Om åtgärden väntar, anges händelseobjektet i den angivna OVERLAPPED strukturen till icke-signalerat tillstånd innan funktionen returneras. När den väntande åtgärden är klar anger systemet tillståndet för händelseobjektet till signalerat. Tillståndet för händelseobjektet ändras inte om åtgärden slutförs innan funktionen returneras.
Eftersom exemplet använder händelseobjekt för manuell återställning ändras inte tillståndet för ett händelseobjekt till icke-signalerat av funktionen WaitForMultipleObjects. Detta är viktigt eftersom exemplet förlitar sig på att händelseobjekten förblir i signalerat tillstånd, förutom när det finns en väntande åtgärd.
Om åtgärden redan har slutförts när ReadFile, WriteFileeller ConnectNamedPipe returnerar, anger funktionens returvärde resultatet. För läs- och skrivåtgärder returneras även antalet överförda byte. Om åtgärden fortfarande väntar returnerar funktionen ReadFile, WriteFileeller ConnectNamedPipe noll och funktionen GetLastError returnerar ERROR_IO_PENDING. I det här fallet använder du funktionen GetOverlappedResult för att hämta resultatet när åtgärden har slutförts. GetOverlappedResult returnerar endast resultatet av pågående åtgärder. Det rapporterar inte resultaten av operationer som slutfördes innan funktionen ReadFile, WriteFileeller ConnectNamedPipe returnerades.
Innan du kopplar från en klient måste du vänta på en signal som anger att klienten är klar. (Tömning av filbuffertar skulle motverka syftet med överlappande I/O eftersom tömningsåtgärden skulle blockera körningen av servertråden medan den väntar på att klienten ska tömma röret.) I det här exemplet är signalen det fel som genereras genom att försöka läsa från röret när rörklienten stänger sitt handtag.
#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);
}
Relaterade ämnen