Server Using Completion Routines
The
ReadFileEx and
WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped
ReadFile and
WriteFile functions, which use an event object to signal completion, the extended
functions specify a completion routine. A completion routine is a function that is
queued for execution when the read or write operation is done. The completion
routine is not executed until the thread that called
ReadFileEx and
WriteFileEx enters an alertable wait by calling one of the extended wait functions:
WaitForSingleObjectEx,
WaitForMultipleObjectsEx, or
SleepEx. These functions are like the other wait functions in that they return when a
specified object is in the signaled state or when a time-out interval has
elapsed. However, these functions can also perform an alertable wait, which occurs
when their
fAlertable parameter is set to TRUE. In an alertable wait, the functions also return
when a
ReadFileEx or
WriteFileEx completion routine is queued for execution. A server process can use the
extended functions to perform a sequence of read and write operations for each
client that connects to it. Each read or write operation in the sequence specifies
a completion routine, and each completion routine initiates the next step in
the sequence.
Like the previous example, this example is a single-threaded server process
that creates a message-type pipe and uses overlapped operations. The server
process differs in that it uses the extended functions
ReadFileEx and
WriteFileEx to perform overlapped I/O. Unlike the overlapped
ReadFile and
WriteFile functions, which signal an event object upon completion, the extended
functions specify a completion routine, which is queued for execution when the
operation is finished. The server process uses the
WaitForSingleObjectEx function, which performs an alertable wait that returns when a completion
routine is ready to execute. The wait function also returns when an event object
is signaled, which in this example indicates that the overlapped
ConnectNamedPipe operation has finished (a new client has connected).
Initially, the server process creates a single instance of the pipe and starts
an overlapped
ConnectNamedPipe operation. When a client connects, the server allocates a structure to
provide storage for that pipe instance and then calls the
ReadFileEx function to start a sequence of I/O operations to handle communications with
the client. Each operation specifies a completion routine that performs the
next operation in the sequence. The sequence terminates when the client is
disconnected and the pipe instance closed. After starting the sequence of operations
for the new client, the server creates another pipe instance and waits for the
next client to connect.
The parameters of the
ReadFileEx and
WriteFileEx functions specify a completion routine and a pointer to an
OVERLAPPED structure. This pointer is later passed to the completion routine in its
lpOverLap parameter. Because the
OVERLAPPED structure points to the first member in the structure allocated for each pipe
instance, the completion routine can use its
lpOverLap parameter to access the structure for the pipe instance.
To avoid duplication, the listing for the ConnectToNewClient subroutine is not
shown; it is identical to the one used by the overlapped server process in the
previous section.
#include <windows.h>
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;
BOOL CreateAndConnectInstance();
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);
VOID DisconnectAndClose(LPPIPEINST);
VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
HANDLE hPipe;
DWORD main(VOID)
{
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbBytes;
BOOL fSuccess, fPendingIO;
// Create one event object for the connect operation.
hConnectEvent = CreateEvent(
NULL, // no security attribute
TRUE, // manual reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hConnectEvent == NULL)
MyErrExit("CreateEvent");
oConnect.hEvent = hConnectEvent;
// Call a subroutine to create one instance, and wait for
// the client to connect.
fPendingIO = CreateAndConnectInstance(&oConnect);
while (1)
{
// Wait for a client to connect, or for a read or write
// operation to be completed, which causes a completion
// routine to be queued for execution.
dwWait = WaitForSingleObjectEx(
hConnectEvent, // event object to wait for
INFINITE, // waits indefinitely
TRUE); // alertable wait enabled
switch (dwWait)
{
// The wait is satisfied by a completed connect operation.
case 0:
// If an operation is pending, get the result of the
// connect operation.
if (fPendingIO)
{
fSuccess = GetOverlappedResult(
hPipe, // pipe handle
&oConnect, // OVERLAPPED structure
&cbBytes, // bytes transferred
FALSE); // does not wait
if (!fSuccess)
MyErrExit("ConnectNamedPipe");
}
// Allocate storage for this instance.
lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
MyErrExit("GlobalAlloc lpPipeInst");
lpPipeInst->hPipeInst = hPipe;
// Start the read operation for this client.
// Note that this same routine is later used as a
// completion routine after a write operation.
lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst);
// Create new pipe instance for the next client.
fPendingIO = CreateAndConnectInstance(
&oConnect);
break;
// The wait is satisfied by a completed read or write
// operation. This allows the system to execute the
// completion routine.
case WAIT_IO_COMPLETION:
break;
// An error occurred in the wait function.
default:
MyErrExit("WaitForSingleObjectEx");
}
}
return 0;
}
// CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as a completion routine after writing to
// the pipe, or when a new client has connected to a pipe instance. It
// starts another read operation.
VOID WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The write operation has finished, so read the next request (if
// there is no error).
if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
BUFSIZE,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
// Disconnect if an error occurred.
if (! fRead)
DisconnectAndClose(lpPipeInst);
}
// CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as an I/O completion routine after reading a
// request from the client. It gets data and writes it to the pipe.
VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The read operation has finished, so write a response (if no
// error occurred).
if ((dwErr == 0) && (cbBytesRead != 0))
{
GetDataToWriteToClient(lpPipeInst);
fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
lpPipeInst->cbToWrite,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}
// Disconnect if an error occurred.
if (! fWrite)
DisconnectAndClose(lpPipeInst);
}
// DisconnectAndClose(LPPIPEINST)
// This routine is called when an error occurs or the client closes
// its handle to the pipe.
VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
// Disconnect the pipe instance.
if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
MyErrExit("DisconnectNamedPipe");
// Close the handle to the pipe instance.
CloseHandle(lpPipeInst->hPipeInst);
// Release the storage for the pipe instance.
if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}
// CreateAndConnectInstance(LPOVERLAPPED)
// This function creates a pipe instance and connects to the client.
// It returns TRUE if the connect operation is pending and FALSE if
// the connection has been completed.
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";
hPipe = 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
PIPE_UNLIMITED_INSTANCES, // unlimited instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attributes
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");
// Call a subroutine to connect to the new client.
return ConnectToNewClient(hPipe, lpoOverlap);
}
- 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