Windows API 进程间通信,管道(Pipe)

来源:互联网 发布:002063 远光软件 编辑:程序博客网 时间:2024/05/11 01:04

转载自:Windows API 进程间通信,管道(Pipe)

    管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。

    管道分为匿名管道和命名管道。

    匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。

    命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。

一、注意点

1、常用API

Pipes[2]

[3,4]中也对这一部分进行了介绍。

2、示例

1)服务器端

创建管道 >> 监听 >>读写 >> 关闭

CreateNamedPipe

ConnectNamedPipe

ReadFile/WriteFile

DisconnectNamedPipe

示例代码

复制代码
通过pipe进程间通信**************************************//* 头文件 */#include <windows.h> #include <stdio.h>#include <tchar.h>/* 常量 */#define PIPE_TIMEOUT 5000#define BUFSIZE 4096/* 结构定义 */typedef struct { OVERLAPPED oOverlap; HANDLE hPipeInst; TCHAR chRequest[BUFSIZE]; DWORD cbRead; TCHAR chReply[BUFSIZE]; DWORD cbToWrite; } PIPEINST, *LPPIPEINST; /* 函数声明 */VOID DisconnectAndClose(LPPIPEINST); BOOL CreateAndConnectInstance(LPOVERLAPPED); BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED); VOID GetAnswerToRequest(LPPIPEINST); VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED); VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED); /* 全局变量 */HANDLE hPipe; /* ************************************* int main(VOID) * 功能 pipe 通信服务端主函数**************************************/int main(VOID) { HANDLE hConnectEvent; OVERLAPPED oConnect; LPPIPEINST lpPipeInst; DWORD dwWait, cbRet; BOOL fSuccess, fPendingIO; // 用于连接操作的事件对象 hConnectEvent = CreateEvent( NULL, // 默认属性 TRUE, // 手工reset TRUE, // 初始状态 signaled NULL); // 未命名 if (hConnectEvent == NULL) { printf("CreateEvent failed with %d.\n", GetLastError()); return0; } // OVERLAPPED 事件 oConnect.hEvent = hConnectEvent; // 创建连接实例,等待连接 fPendingIO = CreateAndConnectInstance(&oConnect); while (1) { // 等待客户端连接或读写操作完成 dwWait = WaitForSingleObjectEx( hConnectEvent, // 等待的事件 INFINITE, // 无限等待 TRUE); switch (dwWait) { case0: // pending if (fPendingIO) { // 获取 Overlapped I/O 的结果 fSuccess = GetOverlappedResult( hPipe, // pipe 句柄 &oConnect, // OVERLAPPED 结构 &cbRet, // 已经传送的数据量 FALSE); // 不等待if (!fSuccess) { printf("ConnectNamedPipe (%d)\n", GetLastError()); return0; } } // 分配内存 lpPipeInst = (LPPIPEINST) HeapAlloc(GetProcessHeap(),0,sizeof(PIPEINST)); if (lpPipeInst == NULL) { printf("GlobalAlloc failed (%d)\n", GetLastError()); return0; } lpPipeInst->hPipeInst = hPipe; // 读和写,注意CompletedWriteRoutine和CompletedReadRoutine的相互调用 lpPipeInst->cbToWrite =0; CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst); // 再创建一个连接实例,以响应下一个客户端的连接 fPendingIO = CreateAndConnectInstance( &oConnect); break; // 读写完成 case WAIT_IO_COMPLETION: break; default: { printf("WaitForSingleObjectEx (%d)\n", GetLastError()); return0; } } } return0; } /* ************************************* CompletedWriteRoutine * 写入pipe操作的完成函数* 接口参见FileIOCompletionRoutine回调函数定义** 当写操作完成时被调用,开始读另外一个客户端的请求**************************************/VOID WINAPI CompletedWriteRoutine( DWORD dwErr, DWORD cbWritten, LPOVERLAPPED lpOverLap) { LPPIPEINST lpPipeInst; BOOL fRead = FALSE; // 保存overlap实例 lpPipeInst = (LPPIPEINST) lpOverLap; // 如果没有错误if ((dwErr ==0) && (cbWritten == lpPipeInst->cbToWrite)) { fRead = ReadFileEx( lpPipeInst->hPipeInst, lpPipeInst->chRequest, BUFSIZE*sizeof(TCHAR), (LPOVERLAPPED) lpPipeInst, // 写读操作完成后,调用CompletedReadRoutine (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); } if (! fRead) // 出错,断开连接 DisconnectAndClose(lpPipeInst); } /* ************************************* CompletedReadRoutine * 读取pipe操作的完成函数* 接口参见FileIOCompletionRoutine回调函数定义** 当读操作完成时被调用,写入回复**************************************/VOID WINAPI CompletedReadRoutine( DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap) { LPPIPEINST lpPipeInst; BOOL fWrite = FALSE; // 保存overlap实例 lpPipeInst = (LPPIPEINST) lpOverLap; // 如果没有错误if ((dwErr ==0) && (cbBytesRead !=0)) { // 根据客户端的请求,生成回复 GetAnswerToRequest(lpPipeInst); // 将回复写入到pipe fWrite = WriteFileEx( lpPipeInst->hPipeInst, lpPipeInst->chReply, //将响应写入pipe lpPipeInst->cbToWrite, (LPOVERLAPPED) lpPipeInst, // 写入完成后,调用CompletedWriteRoutine (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine); } if (! fWrite) // 出错,断开连接 DisconnectAndClose(lpPipeInst); } /* ************************************* VOID DisconnectAndClose(LPPIPEINST lpPipeInst) * 功能 断开一个连接的实例* 参数 lpPipeInst,断开并关闭的实例句柄**************************************/VOID DisconnectAndClose(LPPIPEINST lpPipeInst) { // 关闭连接实例if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) ) { printf("DisconnectNamedPipe failed with %d.\n", GetLastError()); } // 关闭 pipe 实例的句柄 CloseHandle(lpPipeInst->hPipeInst); // 释放if (lpPipeInst != NULL) HeapFree(GetProcessHeap(),0, lpPipeInst); } /* ************************************* BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)* 功能 建立连接实例* 参数 lpoOverlap,用于overlapped IO的结构* 返回值 是否成功**************************************/BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) { LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\samplenamedpipe"); // 创建named pipe hPipe = CreateNamedPipe( lpszPipename, // pipe 名 PIPE_ACCESS_DUPLEX |// 可读可写 FILE_FLAG_OVERLAPPED, // overlapped 模式 // pipe模式 PIPE_TYPE_MESSAGE |// 消息类型 pipe PIPE_READMODE_MESSAGE |// 消息读模式 PIPE_WAIT, // 阻塞模式 PIPE_UNLIMITED_INSTANCES, // 无限制实例 BUFSIZE*sizeof(TCHAR), // 输出缓存大小 BUFSIZE*sizeof(TCHAR), // 输入缓存大小 PIPE_TIMEOUT, // 客户端超时 NULL); // 默认安全属性if (hPipe == INVALID_HANDLE_VALUE) { printf("CreateNamedPipe failed with %d.\n", GetLastError()); return0; } // 连接到新的客户端return ConnectToNewClient(hPipe, lpoOverlap); }/* ************************************* BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)* 功能 建立连接实例* 参数 lpoOverlap,用于overlapped IO的结构* 返回值 是否成功**************************************/BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) { BOOL fConnected, fPendingIO = FALSE; // 开始一个 overlapped 连接 fConnected = ConnectNamedPipe(hPipe, lpo); if (fConnected) { printf("ConnectNamedPipe failed with %d.\n", GetLastError()); return0; } switch (GetLastError()) { // overlapped连接进行中. case ERROR_IO_PENDING: fPendingIO = TRUE; break; // 已经连接,因此Event未置位 case ERROR_PIPE_CONNECTED: if (SetEvent(lpo->hEvent)) break; // errordefault: { printf("ConnectNamedPipe failed with %d.\n", GetLastError()); return0; } } return fPendingIO; }// TODO根据客户端的请求,给出响应VOID GetAnswerToRequest(LPPIPEINST pipe){ _tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest); lstrcpyn( pipe->chReply, TEXT("Default answer from server") ,BUFSIZE); pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);}
复制代码

2)客户端

打开命令管道,获得句柄 >> 写入数据 >> 等待回复

WaitNamedPipe

SetNamedPipeHandleState

示例代码

复制代码
通过pipe进程间通信**************************************//* 头文件 */#include <windows.h> #include <stdio.h>#include <conio.h>#include <tchar.h>/* 常量 */#define BUFSIZE 512/* ************************************* int main(VOID) * 功能 pipe 通信服务端主函数**************************************/int main(int argc, TCHAR *argv[]) { HANDLE hPipe; LPTSTR lpvMessage=TEXT("Default message from client"); TCHAR chBuf[BUFSIZE]; BOOL fSuccess; DWORD cbRead, cbWritten, dwMode; LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\samplenamedpipe"); if( argc >1 ) // 如果输入了参数,则使用输入的参数 lpvMessage = argv[1]; while (1) { // 打开一个命名pipe hPipe = CreateFile( lpszPipename, // pipe 名 GENERIC_READ | GENERIC_WRITE, // 可读可写0, // 不共享 NULL, // 默认安全属性 OPEN_EXISTING, // 已经存在(由服务端创建)0, // 默认属性 NULL); if (hPipe != INVALID_HANDLE_VALUE) break; // 如果不是 ERROR_PIPE_BUSY 错误,直接退出 if (GetLastError() != ERROR_PIPE_BUSY) { printf("Could not open pipe"); return0; } // 如果所有pipe实例都处于繁忙状态,等待2秒。if (!WaitNamedPipe(lpszPipename, 2000)) { printf("Could not open pipe"); return0; } } // pipe已经连接,设置为消息读状态 dwMode = PIPE_READMODE_MESSAGE; fSuccess = SetNamedPipeHandleState( hPipe, // 句柄&dwMode, // 新状态 NULL, // 不设置最大缓存 NULL); // 不设置最长时间if (!fSuccess) { printf("SetNamedPipeHandleState failed"); return0; } // 写入pipe fSuccess = WriteFile( hPipe, // 句柄 lpvMessage, // 写入的内容 (lstrlen(lpvMessage)+1)*sizeof(TCHAR), // 写入内容的长度&cbWritten, // 实际写的内容 NULL); // 非 overlapped if (!fSuccess) { printf("WriteFile failed"); return0; } do { // 读回复 fSuccess = ReadFile( hPipe, // 句柄 chBuf, // 读取内容的缓存 BUFSIZE*sizeof(TCHAR), // 缓存大小&cbRead, // 实际读的字节 NULL); // 非 overlapped if (! fSuccess && GetLastError() != ERROR_MORE_DATA) break; //失败,退出 _tprintf( TEXT("%s\n"), chBuf ); // 打印读的结果 } while (!fSuccess); // ERROR_MORE_DATA 或者成功则循环 getch();//任意键退出 // 关闭句柄 CloseHandle(hPipe); return0; }
复制代码

3I/O简介

    I/O模式不仅在进程间通信时使用,任何具有数据流形式的输入输出(包括文件输入输出、内核通信、网络输入输出等)都涉及I/O模式。

    异步( Asynchronous)和同步(Synchronous) I/O是两种基本的I/O模式

同步I/O

所谓同步I/O是指在调用ReadFileWriteFile等函数进行输入输出操作时,系统完成了输入输出ReadFileWriteFile才返回。在操作系统进行I/O操作的过程上,用户态线程不能执行,因此在同步I/O时,如果需要在I/O时进行其他操作就只能再开启线程。

异步I/O

    异步I/O是在调用ReadFileWriteFile等函数后,函数立即返回,线程可以进行其他操作。剩下的I/O操作在系统内核中自动完成。那么在系统内核完成输入输出后,程序如何知道I/O是否已完成?

    一种方法,称作完成函数(Completion Routines),如果使用ReadFileExWriteFileEx等进行I/O,可以指定完成函数,所谓完成函数是指内核在完成I/O后,内核会回调这个函数。当完成函数被调用时,就指明内核已经完成了I/O,程序可以在这个函数中进行一个I/O完成后需要的操作(例如释放内存)

参考

[1] 精通Windows API 函数、接口、编程实例

[2] http://msdn.microsoft.com/en-us/library/aa365137%28VS.85%29.aspx

[3] http://www.cnblogs.com/mydomain/archive/2010/09/18/1830452.html

[4] http://www.cnblogs.com/mydomain/archive/2010/09/04/1818266.html

0 0