管道分为两种,一种是匿名管道,一种是命名管道。两者都可以进行进程间的通信,但匿名管道有局限性,它只能在本机上使用,而不能跨网络使用。但是命名管道就不一样了,它弥补了匿名管道的局限性。接下来看下,如何在windows平台下,进行命名管道的通信。
首先,要进行两个进行间的通信,必须要有一个进程主动一点,来创建一个命名的管道,调用CreateNamedPipe即可创建一个命名的管道,其声明如下:
HANDLE CreateNamedPipe(
LPCTSTR lpName, // 管道名称,形式必须为\\.\pipe\pipeName
DWORD dwOpenMode, // 打开管道的模式
DWORD dwPipeMode, // 管道的模式,传输数据的形式
DWORD nMaxInstances, // 最大连接客户端的个数
DWORD nOutBufferSize, // 输出缓冲区的大小
DWORD nInBufferSize, // 输入缓冲区的大小
DWORD nDefaultTimeOut, // 默认的超时时间
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性,一般为NULL
);
创建管道的进程,我们称之为服务器,当我们创建完管道之后,服务器得知道什么时候有客户端进行连接,我们可以通过一个OVERLAPPED这个结构,该结构里有一个event事件,当有客户端进行连接时,事件对象就变成有信号。有了事件之后,我们就可以调用ConnectNamedPipe来等待一个客户端的连接,其声明如下:
BOOL ConnectNamedPipe(
HANDLE hNamedPipe, // 命名管道对象
LPOVERLAPPED lpOverlapped // OVERLAPPED结构
);
客户端连接之后,两者之间就可以 进行通信了,通信的操作跟我们的文件操作是一样的,通过ReadFile和WriteFile来进行读和写。
通信完之后呢,我们可以调用DisconnectNamedPipe来进行断开连接,其声明如下:
BOOL DisconnectNamedPipe(
HANDLE hNamedPipe // 命名管道对象
);
以下是服务端的测试代码:
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
//创建一个命名管道,在windows中\代表zhuan'yi两个\\代表一个\
HANDLE hNamedPipe = CreateNamedPipeA("\\\\.\\pipe\\testName",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE, 1, 1024, 1024,0 , NULL);
//检查是否创建成功
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
printf("create named pipe failed!\n");
}
else
{
printf("create named pipe success!\n");
}
//异步IO结构
OVERLAPPED op;
ZeroMemory(&op, sizeof(OVERLAPPED));
//创建一个事件内核对象
op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//等待一个客户端进行连接
BOOL b = ConnectNamedPipe(hNamedPipe, &op);
//当有客户端进行连接时,事件变成有信号的状态
if (WaitForSingleObject(op.hEvent, INFINITE) == 0)
{
printf("client connect success!\n");
}
else
{
printf("client connect failed!\n");
}
//连接成功后,进行通信,读写
char buff[100];
sprintf_s(buff, 100, "test message from server!");
DWORD cbWrite;
WriteFile(hNamedPipe, buff, strlen(buff), &cbWrite, NULL);
ZeroMemory(buff, 100);
ReadFile(hNamedPipe, buff, 100, &cbWrite, NULL);
//通信完之后,断开连接
DisconnectNamedPipe(hNamedPipe);
//关闭管道
CloseHandle(hNamedPipe);
system("pause");
return 0;
}
当服务端将管道创建好了之后,客户端就不需要再次创建管道了,客户端只需要连接管道即可,但在连接之前,我们应该调用WaitNamedPipe来检查一下,命名管道是否存在,其声明如下:
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // 管道名称,形式必须为<span style="font-family: Arial, Helvetica, sans-serif;">\\.\pipe\pipeName</span>
DWORD nTimeOut // 超时时间,给NULL为默认的超时时间
);
当检查到命名管道存在了且可用,我们就可以连接管道,连接管道很简单,就是调用CreateFile来打开命名管道。打开之后就可以进行通信了,就是一些文件操作,
以下是客户端的测试代码:
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
//检查命名管道是否存在
BOOL b = WaitNamedPipeA("\\\\.\\pipe\\testName", 0);
//打开管道
HANDLE hFile = CreateFileA("\\\\.\\pipe\\testName",
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//检查是否连接成功
if (!b || hFile == INVALID_HANDLE_VALUE)
{
printf("connect failed!\n");
}
else
{
printf("connect success!\n");
}
//进行通信
char buf[100];
ZeroMemory(buf, 100);
DWORD dwRead;
ReadFile(hFile, buf, 100, &dwRead, NULL);
printf(buf);
WriteFile(hFile, "test message for client!", strlen("test message for client!"), &dwRead, NULL);
//关闭管道
CloseHandle(hFile);
system("pause");
return 0;
}