Named Pipe Server Using Overlapped I/O

The following is an example of a single-threaded pipe server that uses overlapped operations to service simultaneous connections to multiple pipe clients. The pipe server creates a fixed number of pipe instances. Each pipe instance can be connected to a separate pipe client. When a pipe client has finished using its pipe instance, the server disconnects from the client and reuses the pipe instance to connect to a new client. This pipe server can be used with the pipe client described in Named Pipe Client.

The
OVERLAPPED 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 by using the event object in the OVERLAPPED structure. 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 stored in an array that is passed to the
WaitForMultipleObjects function. This function waits for one of the events to be signaled, and returns the array index of the event that caused the wait operation to complete. The example in this topic uses this array 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, which requires a call to the
GetOverlappedResult function. The server uses the dwState member of the structure to determine the next operation that must be performed for the pipe instance.

Overlapped ReadFile, WriteFile, and
ConnectNamedPipe operations can finish by the time the function returns. Otherwise, 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 finishes, 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 an event object 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 has 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 zero and the
GetLastError function returns ERROR_IO_PENDING. In this case, use the GetOverlappedResult function to retrieve the results after the operation has finished. GetOverlappedResult returns only the results of pending operations. It does not report the results of operations that were completed before the overlapped ReadFile, WriteFile, or
ConnectNamedPipe function returned.

Before disconnecting from a client, you must wait for a signal indicating the client has finished. (Flushing the file buffers 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.) In this example, the signal is the error generated by trying to read from the pipe after the pipe client closes its handle.