管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。
管道分为匿名管道和命名管道。
1.匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。
2.命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。
下面主要介绍下常用的命名管道:
1.CreateNamePipe
HANDLE WINAPI CreateNamedPipe(
_In_ LPCTSTR lpName,
_In_ DWORD dwOpenMode,
_In_ DWORD dwPipeMode,
_In_ DWORD nMaxInstances,
_In_ DWORD nOutBufferSize,
_In_ DWORD nInBufferSize,
_In_ DWORD nDefaultTimeOut,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
CreateNamedPipe创建一个命名管道。返回的句柄由管道的服务器端使用
说明
返回值
Long,如执行成功,返回管道的句柄。INVALID_HANDLE_VALUE表示失败。会设置GetLastError
参数表
参数 类型及说明
lpName String,指定管道名,采用的形式是:\\.\管道\管道名。最多可达256个字符的长度,而且不用区分大小写。如果存在指定名字的一个管道,则创建那个管道的一个新实例
dwOpenMode Long,下述常数组的一个组合
下述常数之一(对于管道的所有实例都要一样):
PIPE_ACCESS_DUPLEX 管道是双向的
PIPE_ACCESS_INBOUND 数据从客户端流到服务器端
PIPE_ACCESS_OUTBOUND 数据从服务器端流到客户端
下述常数的任意组合
FILE_FLAG_WRITE_THROUGH 在网络中建立的字节型管道内,强迫数据在每次读写操作的时候通过网络传输。否则传输就可能延迟
FILE_FLAG_OVERLAPPED 允许(但不要求)用这个管道进行异步(重叠式)操作
常数WRITE_DAC, WRITE_OWNER 和 ACCESS_ SYSTEM_SECURITY提供了附加的安全选项
dwPipeMode Long,下述常数组的一个组合:
下述常数之一(管道的所有实例都必须指定相同的常数)
PIPE_TYPE_BYTE 数据作为一个连续的字节数据流写入管道
PIPE_TYPE_MESSAGE 数据用数据块(名为“消息”或“报文”)的形式写入管道
下述常数之一:
PIPE_READMODE_BYTE 数据以单独字节的形式从管道中读出
PIPE_READMODE_MESSAGE 数据以名为“消息”的数据块形式从管道中读出(要求指定PIPE_TYPE_MESSAGE)
下述常数之一:
PIPE_WAIT 同步操作在等待的时候挂起线程
PIPE_NOWAIT(不推荐!) 同步操作立即返回。这样可为异步传输提供一种落后的实现方法,已由Win32的重叠式传输机制取代了
nMaxInstances Long,这个管道能够创建的最大实例数量。必须是1到常数PIPE_UNLIMITED_INSTANCES间的一个值。它对于管道的所有实例来说都应是相同的
nOutBufferSize Long,建议的输出缓冲区长度;零表示用默认设置
nInBufferSize Long,建议的输入缓冲区长度;零表示用默认设置
nDefaultTimeOut Long,管道的默认等待超时。对一个管道的所有实例来说都应相同
lpSecurityAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或者传递零值(将参数声明为ByVal As Long,并传递零值),以便使用不允许继承的一个默认描述符
2.ConnetNamePipe
BOOL WINAPI ConnectNamedPipe(
_In_ HANDLE hNamedPipe,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
ConnectNamedPipe是指示一台服务器等待下去,直至客户机同一个命名管道连接。
返回值
Long,如lpOverlapped为NULL,那么:
如管道已连接,就返回Ture(非零);如发生错误,或者管道已经连接,就返回零(GetLastError此时会返回ERROR_PIPE_CONNECTED)
lpOverlapped有效,就返回零;如管道已经连接,GetLastError会返回ERROR_PIPE_CONNECTED;如重叠操作成功完成,就返回ERROR_IO_PENDING。在这两种情况下,倘若一个客户已关闭了管道,且服务器尚未用DisconnectNamedPipe函数同客户断开连接,那么GetLastError都会返回ERROR_NO_DATA
参数表
参数 类型及说明
hNamedPipe Long,管道的句柄
lpOverlapped OVERLAPPED,如设为NULL(传递ByVal As Long),表示将线程挂起,直到一个客户同管道连接为止。否则就立即返回;此时,如管道尚未连接,客户同管道连接时就会触发lpOverlapped结构中的事件对象。随后,可用一个等待函数来监视连接
下面是一个简单例子:
client:
#include <windows.h>
#include <stdio.h>
#define PIPE_NAME "\\\\.\\Pipe\\MyNamedPipe"
#define BUFFER_SIZE 1024
// 命名管道客户端
int main()
{
HANDLE hPipe;
/*HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName, // 普通文件名或设备文件名
_In_ DWORD dwDesiredAccess, // 访问模式(写/读)
_In_ DWORD dwShareMode, // 共享模式
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,// 指向安全属性的指针
_In_ DWORD dwCreationDisposition, // 如何创建
_In_ DWORD dwFlagsAndAttributes, // 文件属性
_In_opt_ HANDLE hTemplateFile // 用于复制文件句柄
);*/
hPipe = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (INVALID_HANDLE_VALUE == hPipe){
printf("Error occurred while connecting to the server: %d",GetLastError());
return -1;
}else{
printf("CreateFile() was successful.\n");
}
char szBuffer[BUFFER_SIZE] = {0};
printf("Enter a message to be sent to the server\n");
gets_s(szBuffer);
DWORD cbBytes = 0;
BOOL bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
printf("Error occurred while writing to the server: %d\n", GetLastError());
CloseHandle(hPipe);
return -1;
}else{
printf("WriteFile() was successful..\n");
}
bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer),&cbBytes, NULL);
if ((!bResult) || (0 == cbBytes)){
printf("Error occurred while reading from the server: %d\n", GetLastError());
CloseHandle(hPipe);
return -1;
}else{
printf("ReadFile() was successful...\n");
}
printf("Server sent the following message:%s\n",szBuffer);
CloseHandle(hPipe);
return 0;
}
server:
#include <windows.h>
#include <stdio.h>
#define PIPE_NAME "\\\\.\\Pipe\\MyNamedPipe"
#define BUFFER_SIZE 1024
// 命名管道服务端
int main()
{
BOOL bRet = TRUE;
HANDLE hPipe;
// 创建命名管道
hPipe = CreateNamedPipe(
PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFFER_SIZE,
BUFFER_SIZE,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if (INVALID_HANDLE_VALUE == hPipe){
printf("Error occurred while creating the pipe: %d\n", GetLastError());
return -1;
}else{
printf("CreateNamedPipe() was successful.\n");
}
printf("Waiting for client connection...\n");
// 等待连接该管道
BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL);
if (FALSE == bClientConnected){
printf("Error occurred while connecting to the client:%d\n", GetLastError());
CloseHandle(hPipe);
return 1;
}else{
printf("ConnectNamePipe() was successful...\n");
}
PULONG ClientProcessID = 0;
bRet = GetNamedPipeClientProcessId(hPipe, ClientProcessID);
if (FALSE == bRet){
printf("Can not Require processid of NamedPipeClient...\n");
}else{
printf("Current Client Process ID equal %ld\n", ClientProcessID);
}
LPTSTR ClientComputerName = NULL;
ULONG ClientComputerNameLength = 1024;
bRet = GetNamedPipeClientComputerName(hPipe, ClientComputerName, ClientComputerNameLength);
if (FALSE == bRet){
printf("Can not require Client ComputerName of NamedPipeClient\n");
}else{
printf("current Client Computer name of NamedPipe equal %s\n", ClientComputerName);
}
char szBuffer[BUFFER_SIZE] = {0};
DWORD cbBytes = 0;
BOOL bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer), &cbBytes, NULL);
if ((!bResult) || (0 == cbBytes)){
printf("Error occurred while reading from the client:%d\n",GetLastError());
CloseHandle(hPipe);
return 1;
}else{
printf("ReadFile() was successfule...\n");
}
printf("Client sent the following message: %s\n", szBuffer);
strcpy_s(szBuffer, "server back msg");
bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
printf("\n Error occurred while writing to the client: %d", GetLastError());
CloseHandle(hPipe);
return 1;
}else{
printf("WriteFile() was successful...\n");
}
CloseHandle(hPipe);
return 0;
}