Server Using Overlapped Input and Output
This example is a single-threaded server process that uses overlapped
operations to service simultaneous connections to multiple client processes. The server
process creates a fixed number of pipe instances, each of which can be
connected to a separate client process. When a client process has finished using its
pipe instance, the server disconnects from the client and reuses the pipe
instance to connect to a new client.
Associated with each pipe instance is an
OVERLAPPED structure containing an event object. This structure is specified as a
parameter in each
ReadFile,
WriteFile, and
ConnectNamedPipe operation on the pipe instance. Although the example shows simultaneous
operations on different pipe instances, it avoids simultaneous operations on a
single pipe instance. Because the same event object is used for read, write, and
connect operations for each instance, there is no way to know which operation's
completion caused the event to be set to the signaled state for simultaneous
operations using the same pipe instance.
The event handles for each pipe instance are also stored in an array used by
the
WaitForMultipleObjects function. This function waits for one of the events to be signaled, and its
return value is the array index of the event that satisfied the wait. The
example uses this index to retrieve a structure containing information for the pipe
instance. The server uses the
fPendingIO member of the structure to keep track of whether the most recent I/O
operation on the instance was pending, necessitating a call to the
GetOverlappedResult function. It uses the
dwState member of the structure to determine the next operation that must be
performed for the instance.
Overlapped
ReadFile,
WriteFile, and
ConnectNamedPipe operations may have finished when the function returns, or they may still be
pending when the function returns. If the operation is pending, the event
object in the specified
OVERLAPPED structure is set to the nonsignaled state before the function returns. When
the pending operation has finished, the system sets the state of the event
object to signaled. The state of the event object is not changed if the operation
finishes before the function returns.
Because the example uses manual reset event objects, the state of the event
objects is not changed to nonsignaled by the
WaitForMultipleObjects function. This is important, because the example relies on the event objects
remaining in the signaled state except when there is a pending operation.
If the operation is already finished when
ReadFile,
WriteFile, or
ConnectNamedPipe returns, the function's return value indicates the result. For read and write
operations, the number of bytes transferred is also returned. If the operation
is still pending, the
ReadFile,
WriteFile, or
ConnectNamedPipe function returns FALSE and the
GetLastError function returns ERROR_IO_PENDING. In this case, the results are retrieved
using the
GetOverlappedResult function after the operation has finished.
GetOverlappedResult returns only the results of operations that were pending, and does not report
the results of operations that were completed before the overlapped
ReadFile,
WriteFile, or
ConnectNamedPipe function returned.
Before disconnecting from a client, the multithreaded server example in the
previous section used
FlushFileBuffers to ensure that the client had read everything written to the pipe. This would
defeat the purpose of overlapped I/O, because the flush operation would block
the execution of the server thread while it waits for the client to empty the
pipe. Consequently, it is necessary to wait for a signal from the client that it
has finished before disconnecting. In this example, the signal is the error
generated by trying to read from the pipe after the client process closes its
handle.
#include <windows.h>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;
VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
DWORD main(VOID)
{
DWORD i, dwWait, cbBytes, dwErr;
BOOL fSuccess;
LPTSTR lpszPipename = "\\\\.\\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, // no security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
MyErrExit("CreateEvent");
Pipe[i].oOverlap.hEvent = hEvents[i];
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, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attributes
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");
// 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))
MyErrExit("index out of range");
// 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
&cbBytes, // bytes transferred
FALSE); // do not wait
switch (Pipe[i].dwState)
{
// Pending connect operation
case CONNECTING_STATE:
if (! fSuccess)
MyErrExit("ConnectNamedPipe");
Pipe[i].dwState = READING_STATE;
break;
// Pending read operation
case READING_STATE:
if (! fSuccess || cbBytes == 0)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = WRITING_STATE;
break;
// Pending write operation
case WRITING_STATE:
if (! fSuccess || cbBytes != Pipe[i].cbToWrite)
{
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;
default:
MyErrExit("invalid pipe state");
}
}
// 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].chBuf,
BUFSIZE,
&cbBytes,
&Pipe[i].oOverlap);
// The read operation completed successfully.
if (fSuccess && cbBytes != 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:
GetDataToWriteToClient(&Pipe[i]);
fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chBuf,
Pipe[i].cbToWrite,
&cbBytes,
&Pipe[i].oOverlap);
// The write operation completed successfully.
if (fSuccess && cbBytes == 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:
MyErrExit("invalid pipe state");
}
}
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) )
MyErrExit("DisconnectNamedPipe");
// 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 FALSE.
if (fConnected)
MyErrExit("ConnectNamedPipe");
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:
MyErrExit("ConnectNamedPipe");
}
return fPendingIO;
}
- Software for developers
-
Delphi Components
.Net Components
Software for Android Developers
- More information resources
-
MegaDetailed.Net
Unix Manual Pages
Delphi Examples
- Databases for Amazon shops developers
-
Amazon Categories Database
Browse Nodes Database