< Name Pipes APIs & Client Program Example | Name Pipes Main | Overlapped I/O Server & Client Examples >
What do we have in this chapter 15 part 4?
|
The Name Pipe Overlapped Server Example
Create a new empty Win32 console mode application and add the project/solution name.
Add the following source code.
// Purpose: // This sample demonstrates how to develop an advanced named // pipe server that is capable of servicing 5 named pipe // instances. The application is an echo server where data is // received from a client and echoed back to the client. All // the pipe instances are serviced in the main application // thread using Win32 overlapped I/O. |
//
// Command line options: None
//
#include <windows.h>
#include <stdio.h>
#define NUM_PIPES 5
#define BUFFER_SIZE 256
void main(void)
{
HANDLE PipeHandles[NUM_PIPES];
DWORD BytesTransferred;
CHAR Buffer[NUM_PIPES][BUFFER_SIZE];
INT i;
OVERLAPPED Ovlap[NUM_PIPES];
HANDLE Event[NUM_PIPES];
// For each pipe handle instance, the code must maintain the
// pipes' current state, which determines if a ReadFile or
// WriteFile is posted on the named pipe. This is done using
// the DataRead variable array. By knowing each pipe's
// current state, the code can determine what the next I/O operation should be.
BOOL DataRead[NUM_PIPES];
DWORD Ret;
DWORD Pipe;
for(i = 0; i < NUM_PIPES; i++)
{
// Create a named pipe instance
if ((PipeHandles[i] = CreateNamedPipe(L"\\\\.\\Pipe\\jim",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, NUM_PIPES,
0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
return;
}
else
printf("CreateNamedPipe() for pipe %d is OK!\n", i);
// Create an event handle for each pipe instance. This
// will be used to monitor overlapped I/O activity on each pipe
if ((Event[i] = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
{
printf("CreateEvent() for pipe %d failed with error %d\n", i, GetLastError());
continue;
}
else
printf("CreateEvent() for pipe %d is OK!\n", i);
// Maintain a state flag for each pipe to determine when data is to be read from or written to the pipe
DataRead[i] = FALSE;
ZeroMemory(&Ovlap[i], sizeof(OVERLAPPED));
Ovlap[i].hEvent = Event[i];
// Listen for client connections using ConnectNamedPipe()
if (ConnectNamedPipe(PipeHandles[i], &Ovlap[i]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("ConnectNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
printf("Closing handle...\n");
CloseHandle(PipeHandles[i]);
return;
}
}
else
printf("ConnectNamedPipe() for pipe %d is OK!\n", i);
}
printf("Server is now running....\n");
// Read and echo data back to Named Pipe clients forever
while(1)
{
if ((Ret = WaitForMultipleObjects(NUM_PIPES, Event, FALSE, INFINITE)) == WAIT_FAILED)
{
printf("WaitForMultipleObjects() failed with error %d\n", GetLastError());
return;
}
else
printf("WaitForMultipleObjects() is OK!\n");
Pipe = Ret - WAIT_OBJECT_0;
ResetEvent(Event[Pipe]);
// Check overlapped results, and if they fail, reestablish
// communication for a new client; otherwise, process read and write operations with the client
if (GetOverlappedResult(PipeHandles[Pipe], &Ovlap[Pipe], &BytesTransferred, TRUE) == 0)
{
printf("GetOverlapped() result failed %d start over...\n", GetLastError());
if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)
{
printf("DisconnectNamedPipe() failed with error %d\n", GetLastError());
return;
}
else
printf("DisconnectNamedPipe() is OK!\n");
if (ConnectNamedPipe(PipeHandles[Pipe], &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
// Severe error on pipe. Close this handle forever
printf("ConnectNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
printf("Closing handle...\n");
CloseHandle(PipeHandles[Pipe]);
}
else
printf("ConnectNamedPipe() for pipe %d is OK!\n", i);
}
DataRead[Pipe] = FALSE;
}
else
{
// Check the state of the pipe. If DataRead equals
// FALSE, post a read on the pipe for incoming data.
// If DataRead equals TRUE, then prepare to echo data back to the client.
if (DataRead[Pipe] == FALSE)
{
// Prepare to read data from a client by posting a ReadFile operation
ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
Ovlap[Pipe].hEvent = Event[Pipe];
if (ReadFile(PipeHandles[Pipe], Buffer[Pipe], BUFFER_SIZE, NULL, &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("ReadFile() failed with error %d\n", GetLastError());
}
}
else
printf("ReadFile() should be fine!\n");
DataRead[Pipe] = TRUE;
}
else
{
// Write received data back to the client by posting a WriteFile operation.
printf("Received %d bytes, echo bytes back\n", BytesTransferred);
ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
Ovlap[Pipe].hEvent = Event[Pipe];
if (WriteFile(PipeHandles[Pipe], Buffer[Pipe], BytesTransferred, NULL, &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("WriteFile() failed with error %d\n", GetLastError());
}
}
else
printf("WriteFile() should be OK!\n");
DataRead[Pipe] = FALSE;
}
}
}
}
Build and run the project.
-------------------------------------------------------
Create a new empty Win32 console mode application and add the project/solution name.
Add the following source code.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 4096
DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);
int main(int argc, char **argv)
{
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = L"\\\\.\\pipe\\jim";
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the next client connect request
printf("Pipe Server: Awaiting initial client connection on %S\n", lpszPipename);
for (;;)
{
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe() failed, error code %d.\n", GetLastError());
return -1;
}
else
printf("CreateNamedPipe() should be fine!\n");
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED
fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
printf("Client connected, creating a processing thread...\n");
// Create a thread for this client
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
printf("CreateThread() failed, error code %d.\n", GetLastError());
return -1;
}
else
{
printf("CreateThread() should be fine!\n");
CloseHandle(hThread);
}
}
else
// The client could not connect, so close the pipe
CloseHandle(hPipe);
}
return 0;
}
DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming client connections.
{
HANDLE hHeap = GetProcessHeap();
CHAR* pchRequest = (CHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(CHAR));
CHAR* pchReply = (CHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(CHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
// Do some extra error checking in a compound statement, this could be
// made more specific for different recovery routines if more robust
// recovery is needed. For this example we simply quit
if (lpvParam == NULL || pchRequest == NULL || pchReply == NULL)
{
printf( "\nERROR - Pipe Server Failure: InstanceThread() has an unexpected NULL value.\n");
return 0;
}
// The thread's parameter is a handle to a pipe object instance
hPipe = (HANDLE) lpvParam;
// Loop until done reading
while (1)
{
// Read client requests from the pipe
fSuccess = ReadFile(
hPipe, // handle to pipe
pchRequest, // buffer to receive data
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O
if (! fSuccess || cbBytesRead == 0)
break;
GetAnswerToRequest((LPTSTR)pchRequest, (LPTSTR)pchReply, &cbReplyBytes);
// Write the reply to the pipe
fSuccess = WriteFile(
hPipe, // handle to pipe
pchReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O
if (! fSuccess || cbReplyBytes != cbWritten)
break;
}
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
return 1;
}
VOID GetAnswerToRequest(LPTSTR pchRequest, LPTSTR pchReply, LPDWORD pchBytes)
// This routine is a simple function to print the client request to the console
// and populate the reply buffer with a default data string
{
printf("Client Request String:\"%S\"\n", pchRequest);
StringCchCopy(pchReply, BUFSIZE, L"\"Default answer from server\"");
*pchBytes = (lstrlen(pchReply)+1)*sizeof(CHAR);
}
Build and run the project.
Let test this project with the Name Pipe Client Example 2. Firstly, we run the server example.
Then we run the client 2 example. Take note that the received string was truncated.
The server sample output is shown below when communication was completed.
< Name Pipes APIs & Client Program Example | Name Pipes Main | Overlapped I/O Server & Client Examples >