进程间通信——匿名管道
匿名管道只能实现本机机器上两个进程的通信,通常用来在父子进程间通信,不能实现跨网络的通信。
参考孙鑫的深入解析VC++ 第17章,进程间通信:匿名管道
下面只贴出核心代码:
父进程:
void CParentView::OnPipeCreate()
{
SECURITY_ATTRIBUTES sa; //安全结构体
sa.bInheritHandle = TRUE;//子进程可以继承父进程的匿名管道读写句柄
sa.lpSecurityDescriptor = NULL;//系统为创建的匿名管道赋予默认的安全描述符
sa.nLength = sizeof(SECURITY_ATTRIBUTES);//结构体大小,长度成员
if (! CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox(_T("管道创建失败!"));
return;
}
STARTUPINFO sui;//创建新进程所需信息结构体
PROCESS_INFORMATION pi;//进程信息结构体
ZeroMemory(&sui, sizeof(STARTUPINFO));//初始化
sui.cb = sizeof(STARTUPINFO);
sui.dwFlags = STARTF_USESTDHANDLES;
sui.hStdInput = hRead;//将子进程的标准输入设为管道
sui.hStdOutput = hWrite;//将子进程的标准输出设为管道
sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
auto tmp = CreateProcess(_T(".\\Child.exe"), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &sui, &pi);
auto error = GetLastError();
//创建子进程:允许子进程继承父进程句柄
if (!CreateProcess(_T(".\\Child.exe"),NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead = NULL;
hWrite = NULL;
MessageBox(_T("创建子进程失败!"));
return;
}
else
{
//关闭父进程对子进程的引用
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
void CParentView::OnPipeRead()
{
char buf[100];
DWORD dwRread;//保存实际读取的字节数
if (!ReadFile(hRead,buf,100,&dwRread,NULL))//从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((LPCTSTR)(LPTSTR)buf);
}
void CParentView::OnWrite()
{
char buf[] = "首次管道通信成功!";
DWORD dwWrite;//保存实际写入的字节数
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
// TODO: 在此添加命令处理程序代码
}
子进程:
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
hRead = GetStdHandle(STD_INPUT_HANDLE); //获取管道句柄!
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
}
void CChildView::OnPipeRead()
{
char buf[100];
DWORD dwRead;
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((LPCTSTR)(LPTSTR)buf);
}
void CChildView::OnPipeWrite()
{
char buf[] = "匿名管道测试成功!";
DWORD dwWrite;
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
}
匿名管道没有名称,所以只能在父进程调用CreateProcess()函数创建子进程时,将管道的句柄传递给子进程。
父进程通过:
sui.hStdInput = hRead;//将子进程的标准输入设为管道
sui.hStdOutput = hWrite;//将子进程的标准输出设为管道
将匿名管道的句柄作为参数(sui)传递给子进程
子进程通过:
hRead = GetStdHandle(STD_INPUT_HANDLE);
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
获取匿名管道的句柄
利用管道,通过标准的Win32文件系统函数:readfile()、 writefile()进行数据的收发。
进程间通信——有名管道
- 具体过程:
在服务器端调用CreateNamedPipe()创建命名管道后,调用ConnectNamedPipe()函数让服务器等待客户端连接,
在客户端,首先调用WaitNamedPipe()函数判断当前是否有可用的管道实例,如果有,则调用CreateFile()函数打开命名管道实例,建立连接。
CreateFile()函数:创建或打开文件 、 pipes对象,并返回一个可以用来访问这些对象的句柄。
服务的创建命名管道:
void CParentView::OnPipeCreate()
{
//创建命名管道:双向模式、允许重叠方式
//最大实例数值指同一个命名管道最多能创建的实例个数,如果希望同时接受5个客户端请求,需要调用createnamedpipe5次,每个实例某一时刻只能和一个客户端通信
hpipe = CreateNamedPipe(_T("\\\\.\\pipe\\Mypipe"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL);
if (INVALID_HANDLE_VALUE == hpipe)
{
MessageBox(_T("创建命名管道失败!"));
hpipe = NULL;
return;
}
HANDLE hEvent;//匿名人工重置事件对象
hEvent = CreateEvent(NULL, TRUE, false, NULL);
if (!hEvent)
{
MessageBox(_T("创建事件对象失败!"));
CloseHandle(hpipe);
hpipe = NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap, sizeof(OVERLAPPED));
ovlap.hEvent = hEvent;
if (!ConnectNamedPipe(hpipe,&ovlap))
{
if (ERROR_IO_PENDING != GetLastError())
{
MessageBox(_T("等待客户端连接失败!"));
CloseHandle(hpipe);
CloseHandle(hEvent);
hpipe = NULL;
return;
}
}
if (WAIT_FAILED == WaitForSingleObject(hEvent,INFINITE))
{
MessageBox(_T("等待对象失败!"));
CloseHandle(hpipe);
CloseHandle(hEvent);
hpipe = NULL;
return;
}
CloseHandle(hEvent);
}
客户端连接命名管道:
void CChildView::OnPipeConnect()
{
if (!WaitNamedPipe(_T("\\\\.\\pipe\\Mypipe"), NMPWAIT_WAIT_FOREVER))
{
MessageBox(_T("当前没有可利用的命名管道实例!"));
return;
}
hpipe = CreateFile(_T("\\\\.\\pipe\\Mypipe"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hpipe)
{
MessageBox(_T("打开命名管道失败!"));
hpipe = NULL;
return;
}
// TODO: 在此添加命令处理程序代码
}
读写使用,两者是一样的:
void CParentView::OnPipeRead()
{
char buf[100];
DWORD dwRread;//保存实际读取的字节数
if (!ReadFile(hpipe, buf, 100, &dwRread, NULL))//从文件指针指向的位置开始将数据读出到一个文件中, 且支持同步和异步操作,
{
MessageBox(_T("读取数据失败!"));
return;
}
MessageBox((LPCTSTR)(LPTSTR)buf);
}
void CParentView::OnWrite()
{
char buf[] = "管道通信成功!";
DWORD dwWrite;//保存实际写入的字节数
if (!WriteFile(hpipe, buf, strlen(buf) + 1, &dwWrite, NULL))
{
MessageBox(_T("写入数据失败!"));
return;
}
}


1294

被折叠的 条评论
为什么被折叠?



