Using Pipes for IPC
There are two types of pipes for two-way communication: anonymous pipes and named pipes.
Typically, an anonymous pipe is used for redirecting the standard input or output of a child process so that it can exchange data with its parent process. Anonymous pipes cannot be used over a network, nor can they be used between unrelated processes.
Named pipes are used to transfer data between processes that are not related processes and between processes on different computers.
本质上A pipe is a section of shared memory that processes use for communication. The process that creates a pipe is the pipe server. A process that connects to a pipe is a pipe client. One process writes information to the pipe, then the other process reads the information from the pipe.
The term pipe server refers to a process that creates a named pipe, and the term pipe client refers to a process that connects to an instance of a named pipe. The server-side function for instantiating a named pipe is CreateNamedPipe. The server-side function for accepting a connection is ConnectNamedPipe. A client process connects to a named pipe by using the CreateFile or CallNamedPipe function.
Pipe Names
\\ServerName\pipe\PipeName
where ServerName is either the name of a remote computer or a period, to specify the local computer. The pipe name string specified by PipeName can include any character other than a backslash, including numbers and special characters. The entire pipe name string can be up to 256 characters long. Pipe names are not case-sensitive.
Overlapped operations make it possible for one pipe to read and write data simultaneously and for a single thread to perform simultaneous I/O operations on multiple pipe handles. This enables a single-threaded pipe server to handle communications with multiple pipe clients efficiently.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365799(v=vs.85).aspx
The following examples demonstrate the use of pipes to pass a continuous stream of data between processes:
- Multithreaded pipe server
- Named pipe server using overlapped I/O
- Named pipe server using completion routines
- Named pipe client
- Transactions on named pipes
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define BUFSIZE 512
DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);
int _tmain(int argc, _TCHAR* argv[])
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
// 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. It is an infinite loop.
for (;;)
{
_tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
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)
{
_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
/********************Wait for the client to connect****************************
ConnectNamedPipe function
Enables a named pipe server process to wait for a client process to connect to an instance of a named pipe.
A client process connects by calling either the CreateFile or CallNamedPipe function.
*/
// if connected, 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)
{
_tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
return -1;
}
else
CloseHandle(hThread);
}
else
{
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
}
return 0;
}
// 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.
DWORD WINAPI InstanceThread(LPVOID lpvParam)
{
HANDLE hHeap = GetProcessHeap();
TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
if (lpvParam == NULL)
{
printf("\nERROR - Pipe Server Failure:\n");
printf(" InstanceThread got an unexpected NULL value in lpvParam.\n");
printf(" InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
return (DWORD)-1;
}
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE)lpvParam;
if (pchRequest == NULL)
{
printf("\nERROR - Pipe Server Failure:\n");
printf(" InstanceThread got an unexpected NULL heap allocation.\n");
printf(" InstanceThread exitting.\n");
if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
CloseHandle(hPipe);
return (DWORD)-1;
}
if (pchReply == NULL)
{
printf("\nERROR - Pipe Server Failure:\n");
printf(" InstanceThread got an unexpected NULL heap allocation.\n");
printf(" InstanceThread exitting.\n");
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
CloseHandle(hPipe);
return (DWORD)-1;
}
// Print verbose messages. In production code, this should be for debugging only.
printf("InstanceThread created, receiving and processing messages.\n");
// Loop until done reading**********************waiting with a keep trying-read loop *************************
while (1)
{
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length
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)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
_tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
}
else
{
_tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
}
break;
}
// ****************handle imcoming message immediately and write back to pipe as reply
// Process the incoming message.
GetAnswerToRequest(pchRequest, 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)
{
_tprintf(TEXT("InstanceThread WriteFile failed, GLE=%d.\n"), GetLastError());
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); //Typically the WriteFile and WriteFileEx functions write data to an internal buffer that the operating system writes to a disk or communication pipe on a regular basis.
//The FlushFileBuffers function writes all the buffered information for a specified file to the device or pipe.
DisconnectNamedPipe(hPipe); //If the client end of the named pipe is open, the DisconnectNamedPipe function forces that end of the named pipe closed.
//The client receives an error the next time it attempts to access the pipe.
CloseHandle(hPipe);
HeapFree(hHeap, 0, pchRequest);
HeapFree(hHeap, 0, pchReply);
printf("InstanceThread exitting.\n");
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. This is where you
// would put the actual client request processing code that runs in the context
// of an instance thread. Keep in mind the main thread will continue to wait for
// and receive other client connections while the instance thread is working.
{
_tprintf(TEXT("Client Request String:\"%s\"\n"), pchRequest);
// Check the outgoing message to make sure it's not too long for the buffer.
if (FAILED(StringCchCopy(pchReply, BUFSIZE, TEXT("default answer from server"))))
{
*pchBytes = 0;
pchReply[0] = 0;
printf("StringCchCopy failed, no outgoing message.\n");
return;
}
*pchBytes = (lstrlen(pchReply) + 1)*sizeof(TCHAR);
}
2) Named Pipe Client
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hPipe;
LPTSTR lpvMessage = TEXT("Default message from client.");
TCHAR chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if (argc > 1)
lpvMessage = argv[1];
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
hPipe = CreateFile(
lpszPipename, //pipe name
GENERIC_READ | GENERIC_WRITE, //read and write access
0, //no sharing
NULL, //default security attributes
OPEN_EXISTING, //opens existing pipe
0, //default attributes
NULL); //no template file
// Break if the pipe handle is valid.
// Jump out off loop, since the valid pipe handle has been gotten
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
_tprintf(TEXT("Could not open pipe. GLE=%d\n"), GetLastError());
return -1;
}
// All pipe instances are busy, so wait for 20 seconds.
if (!WaitNamedPipe(lpszPipename, 20000))
{
printf("Could not open pipe: 20 second wait timed out.");
return -1;
}
}
//When code comes here, one valid pipe handle has been gotten
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if (!fSuccess)
{
_tprintf(TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError());
return -1;
}
// Send a message to the pipe server.
cbToWrite = (lstrlen(lpvMessage) + 1)*sizeof(TCHAR);
_tprintf(TEXT("Sending %d byte message: \"%s\"\n"), cbToWrite, lpvMessage);
fSuccess = WriteFile(
hPipe, // pipe handle
lpvMessage, // message
cbToWrite, // message length
&cbWritten, // bytes written
NULL); // not overlapped
if (!fSuccess)
{
_tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
return -1;
}
printf("\nMessage sent to server, receiving reply as follows:\n");
do
{
// Read from the pipe.
/*
The ReadFile function returns when one of the following conditions occur:
•The number of bytes requested is read.
•A write operation completes on the write end of the pipe.
•An asynchronous handle is being used and the read is occurring asynchronously.
•An error occurs.
*/
fSuccess = ReadFile(
hPipe, // pipe handle
chBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbRead, // number of bytes read
NULL); // not overlapped
if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
break;
_tprintf(TEXT("\"%s\"\n"), chBuf);
} while (!fSuccess); // repeat loop if ERROR_MORE_DATA
if (!fSuccess)
{
_tprintf(TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError());
return -1;
}
printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();
CloseHandle(hPipe);
return 0;
}