进程间通讯-命名管道
前置知识:学习了邮槽的知识,了解CreateFile、ReadFile、WriteFile函数的使用方法。
命名管道是一种不但能在同一机器上实现两个进程通讯,还能在网络中不同机器上的两个进程之间通讯的机制。与邮槽不同,命名管道传输数据是采取基于连接并且可靠的传输方式,所以命名管道传输数据只能一堆一进行传输。
1.创建命名管道
创建命名管道可以用CreateNamedPipe()函数,函数原型如下:
HANDLE CreateNamedPipe(
LPCTSTR lpName, // pipe name
DWORD dwOpenMode, // pipe open mode
DWORD dwPipeMode, // pipe-specific modes
DWORD nMaxInstances, // maximum number of instances
DWORD nOutBufferSize, // output buffer size
DWORD nInBufferSize, // input buffer size
DWORD nDefaultTimeOut, // time-out interval
LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
);
如果调用成功,则返回创建命名管道的句柄。否则,函数返回INVALID_HANDLE_VALUE。
- 参数lpName表示创建的命名管道名称。格式为“\\.\pipe\pipename”。在VC环境下应修改为“\\\\.\\pipe\\pipename”,如果用户希望在不同的计算机的两个进程之间通讯,可以将“.”换成远程计算机的名称。
- 参数dwOpenMode表示命名管道的打开方式,该参数取值如下表:
模 式 取 值
模 式 说 明
PIPE_ACCESS_DUPLEX
指定双向模式,服务端与客户端都可以从命名管道中读取或写入数据
PIPE_ACCESS_INBOUND
命名管道的数据只能从客户端到服务端,即服务端只能读取数据,客户端只能写入数据
PIPE_ACCESS_OUTBOUND
命名管道的数据只能从服务端到客户端,即服务端只能写入数据,客户端只能读取数据
FILE_FLAG_WRITE_THROUGH
允许写直通模式。当用户指定该值时,写入数据的一方要等到写入的数据到达另一方的数据缓冲区之后,才会成功返回
FILE_FLAG_OVERLAPPED
允许使用重叠模式。采用该模式可以使一些耗费时间的操作在后台执行,在重叠模式下,一个线程可以在多个管道实例上同时处理输入与输出
WRITE_DAC
调用线程对命名管道的任意访问控制列表都可以进行写入操作
WRITE_OWNER
调用者对命名管道的所有者可以进行写入操作
ACCESS_SYSTEM_SECURITY
调用者对命名管道的安全访问控制列表可以进行写入操作
- 参数dwPipeMode表示句柄管道的类型、读取以及等待方式,该参数取值如下表:
取 值
说 明
PIPE_TYPE_BYTE
数据以字节流的形式写入管道
PIPE_TYPE_MESSAGE
数据以消息流的形式写入管道
PIPE_READMODE_BYTE
以字节流的形式从管道中读取数据
PIPE_READMODE_MESSAGE
以消息流的形式从管道中读取数据
PIPE_WAIT
允许阻塞模式
PIPE_NOWAIT
允许非阻塞模式
- 参数nMaxInstances表示管道能够创建实例的最大数目。其取值范围在1~PIPE_UNLIMITED_INSTANCES。如果将该值设为PIPE_UNLIMITED_INSTANCES,则创建的管道实例数据仅限于操作系统。
- 参数nOutBufferSize表示输出缓冲区的大小。
- 参数nInBufferSize表示输入缓冲区的大小。
- 参数nDefaultTimeOut表示超时值,使用同一管道的不同实例必须将该参数取同样的超时值。
- 参数lpSecurityAttributes是指向结构体SECURITY_ATTRIBUTES的指针,表示命名管道的安全属性。
例子:创建一个命名管道
HANDLE hpip;
hpip=CreateNamedPipe("\\\\.\\pipe\\pipename",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCE,1024,1024,0,NULL);
2.连接命名管道
对于服务端而言
可以调用ConnectNamedPipe()函数等待客户端的连接请求。该函数原型如下:
BOOL ConnectNamedPipe(
HANDLE hNamedPipe, // handle to named pipe
LPOVERLAPPED lpOverlapped // overlapped structure
);
该函数只能对命名管道的服务器方进行调用,其作用是等待客户端的连接请求。
- 参数hNamedPipe表示命名管道的句柄。
- 参数lpOverlapped是指向结构体OVERLAPPED的指针,如果创建的管道是使用FILE_FLAG_OVERLAPPED标记打开,那么该参数指向的结构体中必须包含一个人工重置的事件对象。
例子:用户在服务器端使用该函数等待客户端的连接请求
OVERLAPPED ovi={0};
If (::ConnectNamedPipe(hpip,&ovi)){
MessageBox("客户端连接成功");
}
对于客户端而言
需要在连接服务端创建的命名管道之前判断该命名管道是否可用,所以需要使用WaitNamedPipe()函数。该函数原型如下:
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // pipe name
DWORD nTimeOut // time-out interval
);
该函数的作用是判断服务端创建的命名管道是否可用。
- 参数lpNamedPipeName表示命名管道的名称。
- 参数nTimeOut表示超时的时间间隔。其取值如下表所示:
取 值
说 明
NMPWAIT_USE_DEFAULT_WAIT
表示超时时间是服务端创建命名管道时所指定的超时时间
NMPWAIT_WAIT_FOREVER
表示该函数将一直等待,直到出现可用的命名管道
当函数调用成功反对TRUE,调用失败返回FALSE。
接着,客户端使用函数CreateFile()将该命名管道打开以获得该管道的句柄。
3.读写命名管道
不论服务端还是客户端,只要双方命名的管道连接成功,用户便可以调用函数ReadFile()和WriteFile()对命名管道进行读写操作。
4.命名管道服务端实例
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hpip;
OVERLAPPED ovi={0};
char buf[200];
DWORD readbuf;
hpip=CreateNamedPipe("\\\\.\\pipe\\pipename", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
printf("创建管道成功,正在等待客户端连接!\r\n");
if (::ConnectNamedPipe(hpip, &ovi)){
printf("客户端连接成功!\r\n");
printf("正在读取数据!\r\n");
}
if (::ReadFile(hpip, buf, 200, &readbuf, NULL)){
printf("数据读取成功!\r\n");
printf("读取的数据是:%s\r\n",buf);
}
else{
printf("读取数据失败\r\n");
}
return 0;
}
5.命名管道客户端实例
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hpip;
OVERLAPPED ovi={0};
char buf[]="blog.csdn.net/encal";
DWORD readbuf;
printf("正在连接命名管道!\r\n");
if (::WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_WAIT_FOREVER)){
hpip=CreateFile("\\\\.\\pipe\\pipename", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hpip == INVALID_HANDLE_VALUE){
printf("打开命名管道失败\r\n");
}
else{
if (WriteFile(hpip, buf, sizeof(buf), &readbuf, NULL)){
printf("数据写入成功\r\n");
}
else{
printf("数据写入失败\r\n");
}
}
}
else{
printf("连接命名管道失败\r\n");
}
return 0;
}