技术:IPC,RPC,Windows General
主题:Named Pipe,Inter-process Communication
概要:
命名管道是一种进程间单工或双工的通信机制。它可以在管道服务器和一个或多个管道客户端间进行。客户端可以位于本机或互联网上的远程计算机。
PIPE_ACCESS_INBOUND(呼入):
Client (GENERIC_WRITE) ---> Server (GENERIC_READ)
PIPE_ACCESS_OUTBOUND(外传):
Client (GENERIC_READ) <--- Server (GENERIC_WRITE)
PIPE_ACCESS_DUPLEX(双工):
Client (GENERIC_READ or GENERIC_WRITE, or both) <-->
Server (GENERIC_READ and GENERIC_WRITE)
GENERIC_READ(普通读)
GENERIC_WRITE(普通写)
下面的代码示例演示了如何调用CreateNamedPipe来创建一个名称为"\\.\pipe\SamplePipe", 的管道。支持全双工连接。这样客户端和服务端都可以从管道中读写数据。自定义安全选项使得认证的用户才具有对管道的读写权限。当有客户端连接管道时,服务端尝试调用ReadFile从管道中读出客户端的消息,并通过调用WriteFile写入响应消息。
如何演示:
1.在VS2008中编译CppNamedPipeClient 和CppNamedPipeServer 两个工程,如果成功你会获得两个可执行文件CppNamedPipeClient.exe 和 CppNamedPipeServer.exe.
2.运行CppNamedPipeServer.exe。如果管道创建成功,程序会以命令行形式输出以下信息:
Server:
The named pipe (\\.\pipe\SamplePipe) is created.
Waiting for the client's connection...
3.运行CppNamedPipeClient.exe。如果客户端成功连接到命名管道会输出下列信息:
Client:
The named pipe (\\.\pipe\SamplePipe) is connected.
同时服务器端会输出下面的消息来指示有一个客户端连接到管道
Server:
Client is connected.
4.接下来客户端会尝试写入消息到命名管道,程序输出:
Client:
Send 56 bytes to server: "Default request from client"
当服务端从客户端读取消息后打印出:
Server:
Receive 56 bytes from client: "Default request from client"
接下来,服务端写入一个回应消息到管道。
Server:
Send 58 bytes to client: "Default response from server"
然后客户端收到回应消息输出:
Client:
Receive 58 bytes from server: "Default response from server"
最后断开连接,关闭管道。
主要代码逻辑:
1.调用CreateNamedPipe创建一个命名管道,指明管道的名称,方向,传输模式,安全属性等
完整代码:
主题:Named Pipe,Inter-process Communication
概要:
命名管道是一种进程间单工或双工的通信机制。它可以在管道服务器和一个或多个管道客户端间进行。客户端可以位于本机或互联网上的远程计算机。
PIPE_ACCESS_INBOUND(呼入):
Client (GENERIC_WRITE) ---> Server (GENERIC_READ)
PIPE_ACCESS_OUTBOUND(外传):
Client (GENERIC_READ) <--- Server (GENERIC_WRITE)
PIPE_ACCESS_DUPLEX(双工):
Client (GENERIC_READ or GENERIC_WRITE, or both) <-->
Server (GENERIC_READ and GENERIC_WRITE)
GENERIC_READ(普通读)
GENERIC_WRITE(普通写)
下面的代码示例演示了如何调用CreateNamedPipe来创建一个名称为"\\.\pipe\SamplePipe", 的管道。支持全双工连接。这样客户端和服务端都可以从管道中读写数据。自定义安全选项使得认证的用户才具有对管道的读写权限。当有客户端连接管道时,服务端尝试调用ReadFile从管道中读出客户端的消息,并通过调用WriteFile写入响应消息。
如何演示:
1.在VS2008中编译CppNamedPipeClient 和CppNamedPipeServer 两个工程,如果成功你会获得两个可执行文件CppNamedPipeClient.exe 和 CppNamedPipeServer.exe.
2.运行CppNamedPipeServer.exe。如果管道创建成功,程序会以命令行形式输出以下信息:
Server:
The named pipe (\\.\pipe\SamplePipe) is created.
Waiting for the client's connection...
3.运行CppNamedPipeClient.exe。如果客户端成功连接到命名管道会输出下列信息:
Client:
The named pipe (\\.\pipe\SamplePipe) is connected.
同时服务器端会输出下面的消息来指示有一个客户端连接到管道
Server:
Client is connected.
4.接下来客户端会尝试写入消息到命名管道,程序输出:
Client:
Send 56 bytes to server: "Default request from client"
当服务端从客户端读取消息后打印出:
Server:
Receive 56 bytes from client: "Default request from client"
接下来,服务端写入一个回应消息到管道。
Server:
Send 58 bytes to client: "Default response from server"
然后客户端收到回应消息输出:
Client:
Receive 58 bytes from server: "Default response from server"
最后断开连接,关闭管道。
主要代码逻辑:
1.调用CreateNamedPipe创建一个命名管道,指明管道的名称,方向,传输模式,安全属性等
// Create the named pipe.
hNamedPipe = CreateNamedPipe(
FULL_PIPE_NAME, // Pipe name.
PIPE_ACCESS_DUPLEX, // The pipe is duplex; both server and
// client processes can read from and
// write to the pipe
PIPE_TYPE_MESSAGE | // Message type pipe
PIPE_READMODE_MESSAGE | // Message-read mode
PIPE_WAIT, // Blocking mode is enabled
PIPE_UNLIMITED_INSTANCES, // Max. instances
BUFFER_SIZE, // Output buffer size in bytes
BUFFER_SIZE, // Input buffer size in bytes
NMPWAIT_USE_DEFAULT_WAIT, // Time-out interval
pSa // Security attributes
);
在这个事例中管道支持全双工通信。安全属性允许认证用户具有读写管道权限,所有管理员组成员具有对管道的全部权限
//
// FUNCTION: CreatePipeSecurity(PSECURITY_ATTRIBUTES *)
//
// PURPOSE: The CreatePipeSecurity function creates and initializes a new
// SECURITY_ATTRIBUTES structure to allow Authenticated Users read and
// write access to a pipe, and to allow the Administrators group full
// access to the pipe.
//
// PARAMETERS:
// * ppSa - output a pointer to a SECURITY_ATTRIBUTES structure that allows
// Authenticated Users read and write access to a pipe, and allows the
// Administrators group full access to the pipe. The structure must be
// freed by calling FreePipeSecurity.
//
// RETURN VALUE: Returns TRUE if the function succeeds..
//
// EXAMPLE CALL:
//
// PSECURITY_ATTRIBUTES pSa = NULL;
// if (CreatePipeSecurity(&pSa))
// {
// // Use the security attributes
// // ...
//
// FreePipeSecurity(pSa);
// }
//
BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *ppSa)
2.调用ConnectNamedPipe来等待客户端连接
if (!ConnectNamedPipe(hNamedPipe, NULL))
{
if (ERROR_PIPE_CONNECTED != GetLastError())
{
dwError = GetLastError();
wprintf(L"ConnectNamedPipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
}
3.通过调用ReadFile和WriteFile从管道中读出客户端请求并写入响应数据
//
// Receive a request from client.
//
BOOL fFinishRead = FALSE;
do
{
wchar_t chRequest[BUFFER_SIZE];
DWORD cbRequest, cbRead;
cbRequest = sizeof(chRequest);
fFinishRead = ReadFile(
hNamedPipe, // Handle of the pipe
chRequest, // Buffer to receive data
cbRequest, // Size of buffer in bytes
&cbRead, // Number of bytes read
NULL // Not overlapped I/O
);
if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
{
dwError = GetLastError();
wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Receive %ld bytes from client: \"%s\"\n", cbRead, chRequest);
} while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA
//
// Send a response from server to client.
//
wchar_t chResponse[] = RESPONSE_MESSAGE;
DWORD cbResponse, cbWritten;
cbResponse = sizeof(chResponse);
if (!WriteFile(
hNamedPipe, // Handle of the pipe
chResponse, // Buffer to write
cbResponse, // Number of bytes to write
&cbWritten, // Number of bytes written
NULL // Not overlapped I/O
))
{
dwError = GetLastError();
wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Send %ld bytes to client: \"%s\"\n", cbWritten, chResponse);
4.调用FlushFileBuffers在断开连接之前允许客户端读取管道内容。然后断开客户端连接
FlushFileBuffers(hNamedPipe);
DisconnectNamedPipe(hNamedPipe);
5.关闭管道
CloseHandle(hNamedPipe);
完整代码:
CppNamedPipeServer.cpp
#pragma region Includes
#include <stdio.h>
#include <windows.h>
#include <sddl.h>
#pragma endregion
// The full name of the pipe in the format of \\servername\pipe\pipename.
#define SERVER_NAME L"."
#define PIPE_NAME L"SamplePipe"
#define FULL_PIPE_NAME L"\\\\" SERVER_NAME L"\\pipe\\" PIPE_NAME
#define BUFFER_SIZE 1024
// Response message from client to server. '\0' is appended in the end
// because the client may be a native C++ application that expects NULL
// termiated string.
#define RESPONSE_MESSAGE L"Default response from server"
BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *);
void FreePipeSecurity(PSECURITY_ATTRIBUTES);
int wmain(int argc, wchar_t* argv[])
{
DWORD dwError = ERROR_SUCCESS;
PSECURITY_ATTRIBUTES pSa = NULL;
HANDLE hNamedPipe = INVALID_HANDLE_VALUE;
// Prepare the security attributes (the lpSecurityAttributes parameter in
// CreateNamedPipe) for the pipe. This is optional. If the
// lpSecurityAttributes parameter of CreateNamedPipe is NULL, the named
// pipe gets a default security descriptor and the handle cannot be
// inherited. The ACLs in the default security descriptor of a pipe grant
// full control to the LocalSystem account, (elevated) administrators,
// and the creator owner. They also give only read access to members of
// the Everyone group and the anonymous account. However, if you want to
// customize the security permission of the pipe, (e.g. to allow
// Authenticated Users to read from and write to the pipe), you need to
// create a SECURITY_ATTRIBUTES structure.
if (!CreatePipeSecurity(&pSa))
{
dwError = GetLastError();
wprintf(L"CreatePipeSecurity failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
// Create the named pipe.
hNamedPipe = CreateNamedPipe(
FULL_PIPE_NAME, // Pipe name.
PIPE_ACCESS_DUPLEX, // The pipe is duplex; both server and
// client processes can read from and
// write to the pipe
PIPE_TYPE_MESSAGE | // Message type pipe
PIPE_READMODE_MESSAGE | // Message-read mode
PIPE_WAIT, // Blocking mode is enabled
PIPE_UNLIMITED_INSTANCES, // Max. instances
BUFFER_SIZE, // Output buffer size in bytes
BUFFER_SIZE, // Input buffer size in bytes
NMPWAIT_USE_DEFAULT_WAIT, // Time-out interval
pSa // Security attributes
);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
wprintf(L"Unable to create named pipe w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"The named pipe (%s) is created.\n", FULL_PIPE_NAME);
// Wait for the client to connect.
wprintf(L"Waiting for the client's connection...\n");
if (!ConnectNamedPipe(hNamedPipe, NULL))
{
if (ERROR_PIPE_CONNECTED != GetLastError())
{
dwError = GetLastError();
wprintf(L"ConnectNamedPipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
}
wprintf(L"Client is connected.\n");
//
// Receive a request from client.
//
BOOL fFinishRead = FALSE;
do
{
wchar_t chRequest[BUFFER_SIZE];
DWORD cbRequest, cbRead;
cbRequest = sizeof(chRequest);
fFinishRead = ReadFile(
hNamedPipe, // Handle of the pipe
chRequest, // Buffer to receive data
cbRequest, // Size of buffer in bytes
&cbRead, // Number of bytes read
NULL // Not overlapped I/O
);
if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
{
dwError = GetLastError();
wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Receive %ld bytes from client: \"%s\"\n", cbRead, chRequest);
} while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA
//
// Send a response from server to client.
//
wchar_t chResponse[] = RESPONSE_MESSAGE;
DWORD cbResponse, cbWritten;
cbResponse = sizeof(chResponse);
if (!WriteFile(
hNamedPipe, // Handle of the pipe
chResponse, // Buffer to write
cbResponse, // Number of bytes to write
&cbWritten, // Number of bytes written
NULL // Not overlapped I/O
))
{
dwError = GetLastError();
wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Send %ld bytes to client: \"%s\"\n", cbWritten, chResponse);
// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the client's connection.
FlushFileBuffers(hNamedPipe);
DisconnectNamedPipe(hNamedPipe);
Cleanup:
// Centralized cleanup for all allocated resources.
if (pSa != NULL)
{
FreePipeSecurity(pSa);
pSa = NULL;
}
if (hNamedPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hNamedPipe);
hNamedPipe = INVALID_HANDLE_VALUE;
}
return dwError;
}
//
// FUNCTION: CreatePipeSecurity(PSECURITY_ATTRIBUTES *)
//
// PURPOSE: The CreatePipeSecurity function creates and initializes a new
// SECURITY_ATTRIBUTES structure to allow Authenticated Users read and
// write access to a pipe, and to allow the Administrators group full
// access to the pipe.
//
// PARAMETERS:
// * ppSa - output a pointer to a SECURITY_ATTRIBUTES structure that allows
// Authenticated Users read and write access to a pipe, and allows the
// Administrators group full access to the pipe. The structure must be
// freed by calling FreePipeSecurity.
//
// RETURN VALUE: Returns TRUE if the function succeeds.
//
// EXAMPLE CALL:
//
// PSECURITY_ATTRIBUTES pSa = NULL;
// if (CreatePipeSecurity(&pSa))
// {
// // Use the security attributes
// // ...
//
// FreePipeSecurity(pSa);
// }
//
BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *ppSa)
{
BOOL fSucceeded = TRUE;
DWORD dwError = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pSd = NULL;
PSECURITY_ATTRIBUTES pSa = NULL;
// Define the SDDL for the security descriptor.
PCWSTR szSDDL = L"D:" // Discretionary ACL
L"(A;OICI;GRGW;;;AU)" // Allow read/write to authenticated users
L"(A;OICI;GA;;;BA)"; // Allow full control to administrators
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(szSDDL,
SDDL_REVISION_1, &pSd, NULL))
{
fSucceeded = FALSE;
dwError = GetLastError();
goto Cleanup;
}
// Allocate the memory of SECURITY_ATTRIBUTES.
pSa = (PSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(*pSa));
if (pSa == NULL)
{
fSucceeded = FALSE;
dwError = GetLastError();
goto Cleanup;
}
pSa->nLength = sizeof(*pSa);
pSa->lpSecurityDescriptor = pSd;
pSa->bInheritHandle = FALSE;
*ppSa = pSa;
Cleanup:
// Clean up the allocated resources if something is wrong.
if (!fSucceeded)
{
if (pSd)
{
LocalFree(pSd);
pSd = NULL;
}
if (pSa)
{
LocalFree(pSa);
pSa = NULL;
}
SetLastError(dwError);
}
return fSucceeded;
}
//
// FUNCTION: FreePipeSecurity(PSECURITY_ATTRIBUTES)
//
// PURPOSE: The FreePipeSecurity function frees a SECURITY_ATTRIBUTES
// structure that was created by the CreatePipeSecurity function.
//
// PARAMETERS:
// * pSa - pointer to a SECURITY_ATTRIBUTES structure that was created by
// the CreatePipeSecurity function.
//
void FreePipeSecurity(PSECURITY_ATTRIBUTES pSa)
{
if (pSa)
{
if (pSa->lpSecurityDescriptor)
{
LocalFree(pSa->lpSecurityDescriptor);
}
LocalFree(pSa);
}
}
CppNamedPipeClient.cpp
#pragma region Includes
#include <stdio.h>
#include <windows.h>
#pragma endregion
// The full name of the pipe in the format of \\servername\pipe\pipename.
#define SERVER_NAME L"."
#define PIPE_NAME L"SamplePipe"
#define FULL_PIPE_NAME L"\\\\" SERVER_NAME L"\\pipe\\" PIPE_NAME
#define BUFFER_SIZE 1024
// Request message from client to server.
#define REQUEST_MESSAGE L"Default request from client"
int wmain(int argc, wchar_t* argv[])
{
HANDLE hPipe = INVALID_HANDLE_VALUE;
DWORD dwError = ERROR_SUCCESS;
// Try to open the named pipe identified by the pipe name.
while (TRUE)
{
hPipe = CreateFile(
FULL_PIPE_NAME, // 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
);
// If the pipe handle is opened successfully ...
if (hPipe != INVALID_HANDLE_VALUE)
{
wprintf(L"The named pipe (%s) is connected.\n", FULL_PIPE_NAME);
break;
}
dwError = GetLastError();
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (ERROR_PIPE_BUSY != dwError)
{
wprintf(L"Unable to open named pipe w/err 0x%08lx\n", dwError);
goto Cleanup;
}
// All pipe instances are busy, so wait for 5 seconds.
if (!WaitNamedPipe(FULL_PIPE_NAME, 5000))
{
dwError = GetLastError();
wprintf(L"Could not open pipe: 5 second wait timed out.");
goto Cleanup;
}
}
// Set the read mode and the blocking mode of the named pipe. In this
// sample, we set data to be read from the pipe as a stream of messages.
DWORD dwMode = PIPE_READMODE_MESSAGE;
if (!SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL))
{
dwError = GetLastError();
wprintf(L"SetNamedPipeHandleState failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
//
// Send a request from client to server
//
wchar_t chRequest[] = REQUEST_MESSAGE;
DWORD cbRequest, cbWritten;
cbRequest = sizeof(chRequest);
if (!WriteFile(
hPipe, // Handle of the pipe
chRequest, // Message to be written
cbRequest, // Number of bytes to write
&cbWritten, // Number of bytes written
NULL // Not overlapped
))
{
dwError = GetLastError();
wprintf(L"WriteFile to pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Send %ld bytes to server: \"%s\"\n", cbWritten, chRequest);
//
// Receive a response from server.
//
BOOL fFinishRead = FALSE;
do
{
wchar_t chResponse[BUFFER_SIZE];
DWORD cbResponse, cbRead;
cbResponse = sizeof(chResponse);
fFinishRead = ReadFile(
hPipe, // Handle of the pipe
chResponse, // Buffer to receive the reply
cbResponse, // Size of buffer in bytes
&cbRead, // Number of bytes read
NULL // Not overlapped
);
if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
{
dwError = GetLastError();
wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
goto Cleanup;
}
wprintf(L"Receive %ld bytes from server: \"%s\"\n", cbRead, chResponse);
} while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA
Cleanup:
// Centralized cleanup for all allocated resources.
if (hPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hPipe);
hPipe = INVALID_HANDLE_VALUE;
}
return dwError;
}