windows进程通信可以通过很多方式实现,具体可见https://blog.csdn.net/Quellaaa/article/details/83781316。其实进程通信的本质是让两个或多个进程能够看到同一块共同的资源(这块资源一般都是由内存提供)。
本文章主要介绍如何通过命名管道实现跨进程的通信。
1. 管道的初认识
管道分为两种,一种是匿名管道,一种是命名管道。两者都可以进行进程间的通信,但匿名管道有局限性,它只能在本机上使用,而不能跨网络使用。只能用于具有血缘关系的进程间通信,通常用于父子进程建通信 。但是命名管道就不一样了,它弥补了匿名管道的局限性。
简单介绍管道的几个函数:创建、连接和释放函数。
*服务端
·创建管道
要进行两个进行间的通信,必须要有一个进程先创建一个命名的管道,调用CreateNamedPipe即可创建一个命名的管道,其声明如下:
HANDLE CreateNamedPipe(
LPCTSTR lpName, // 管道名称,形式必须为\\.\pipe\pipeName
DWORD dwOpenMode, // 打开管道的模式
DWORD dwPipeMode, // 管道的模式,传输数据的形式
DWORD nMaxInstances, // 最大连接客户端的个数
DWORD nOutBufferSize, // 输出缓冲区的大小
DWORD nInBufferSize, // 输入缓冲区的大小
DWORD nDefaultTimeOut, // 默认的超时时间
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性,一般为NULL
)
参数 lpName 为一个字符串,其格式必须为 \\.\pipe\pipeName ,其中圆点 ”.” 表示的是本地机器
如果想要与远程的服务器建立连接,那么这个圆点位置处应指定这个远程服务器的名称,
而其中的 “pipe” 这个是个固定的字符串,也就是不能改变的,
最后的 “pipename” 则代表的是我将要创建的命名管道的名称了。
·连接管道
服务端(创建管道的进程称为服务端)等待客户端连接请求。我们可以通过一个OVERLAPPED这个结构,该结构里有一个event事件,当有客户端进行连接时,事件对象就变成有信号。有了事件之后,我们就可以调用ConnectNamedPipe来等待一个客户端的连接,其声明如下:
BOOL WINAPI ConnectNamedPipe(
HANDLE hNamedPipe, // 命名管道对象
LPOVERLAPPED lpOverlapped // OVERLAPPED结构
);
*客户端
·判断是否有可以利用的命名管道
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // 管道名称,形式必须为\\.\pipe\pipeName
DWORD nTimeOut // 超时时间,给NULL为默认的超时时间
);
参数 nTimeOut 用来指定超时间隔
NMPWAIT_USE_DEFAULT_WAIT
超时间隔即为服务器端创建该命名管道时指定的超时间隔。
NMPWAIT_USE_DEFAULT_WAIT 一直等待,直到出现一个可用的命名管道的实例。
通过调用该函数可以用来判断是否有可以利用的命名管道,该函数会一直等到,直到等待的时间间隔已过,或者指定的命名管道的实例可以用来连接了,也就是说该管道的服务器进程有正在等待被连接的的 ConnectNamedPipe 操作。
·连接管道
HANDLE CreateFile(
PCTSTR pszName, //文件设备名,此处是命名管道名字
DWORD dwDesiredAccess, //读取方式
DWORD dwShareMode, //共享模式
PSECURITY_ATTRIBUTES psa, //安全属性
DWORD dwCreationgDisposition, //
DWORD dwFlagsAndAttributes,
HANDLE hFileTemplate);
// 该函数具体解释看https://blog.csdn.net/bxsec/article/details/76566011
客户端进程调用 WaitNamedPipe 函数来测试指定名称的管道实例可用后,调用CreateFile 函数连接到一个正在等待连接的命名管道上,在这里客户端需要指定将要连接的命名管道的名称,当 CreateFile 成功返回后,客户进程就得到了一个指向已经建立连接的命名管道实例的句柄,到这里,服务器进程的 ConnectNamedPipe 也就完成了其建立连接的任务。
客户端连接之后,两者之间就可以 进行通信了,通信的操作跟我们的文件操作是一样的,通过ReadFile和WriteFile来进行读和写。
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字节数
LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针
LPOVERLAPPED lpOverlapped
//如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
//该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
);
BOOL WriteFile(
HANDLE hFile,//文件句柄
LPCVOID lpBuffer,//数据缓存区指针
DWORD nNumberOfBytesToWrite,//你要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针
LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针
);
·释放管道
通信完后,我们可以调用DisconnectNamedPipe来进行断开连接,其声明如下:
BOOL DisconnectNamedPipe(
HANDLE hNamedPipe // 命名管道对象
)
2. 管道的代码实现
管道的创建、连接、打开、读写封装成一个类,方便以后使用。 当工程代码中要用到管道通信时,直接调用该类即可,但由于管道的读写通过ReadFile和WriteFile实现,并且这两个函数共有的一个参数lpBuffer类型是void*,因此该类的读写函数用模板类实现。封装后的管道通信cpp文件如下所示:
#include "stdafx.h"
#include "Pipe.h"
#include <iostream>
using std::cout;
#include <windows.h>
CPipe::CPipe( CString strPipeName)
:m_strPipeName(strPipeName)
,m_hNamedPipe(NULL)
{
}
CPipe::~CPipe()
{
if (m_hNamedPipe)
{
Close();
}
}
BOOL CPipe::Create()
{
m_hNamedPipe = CreateNamedPipe(m_strPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE, 1, 1024, 1024,0 , NULL);
//检查是否创建成功
if (m_hNamedPipe == INVALID_HANDLE_VALUE)
{
return FALSE;
}
else
{
return TRUE;
}
}
BOOL CPipe::Connect()
{
//异步IO结构
BOOL fConnected;
OVERLAPPED op;
ZeroMemory(&op, sizeof(OVERLAPPED));
//创建一个事件内核对象
op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//等待一个客户端进行连接
fConnected = ConnectNamedPipe(m_hNamedPipe, &op) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
//当有客户端进行连接时,事件变成有信号的状态
if (WaitForSingleObject(op.hEvent, INFINITE) == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
void CPipe::Close()
{
if (m_hNamedPipe)
{
DisconnectNamedPipe(m_hNamedPipe);
CloseHandle(m_hNamedPipe);
m_hNamedPipe = NULL;
}
}
// 客户端的连接
int CPipe::Open()
{
if(!WaitNamedPipe(m_strPipeName, 0))
return -1;
m_hNamedPipe = CreateFile(m_strPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if ( m_hNamedPipe == INVALID_HANDLE_VALUE)
{
cout << "连接失败\n";
return -2;
}
cout<<"连接成功\n";
return 0;
}
参考文章链接有:https://blog.csdn.net/shufac/article/details/29941239