Windows API 进程间通信,管道(Pipe)
2019独角兽企业重金招聘Python工程师标准>>>
转载自: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()); return 0 ; } // OVERLAPPED 事件 oConnect.hEvent = hConnectEvent; // 创建连接实例,等待连接 fPendingIO = CreateAndConnectInstance( & oConnect); while ( 1 ) { // 等待客户端连接或读写操作完成 dwWait = WaitForSingleObjectEx( hConnectEvent, // 等待的事件 INFINITE, // 无限等待 TRUE); switch (dwWait) { case 0 : // pending if (fPendingIO) { // 获取 Overlapped I/O 的结果 fSuccess = GetOverlappedResult( hPipe, // pipe 句柄 & oConnect, // OVERLAPPED 结构 & cbRet, // 已经传送的数据量 FALSE); // 不等待 if ( ! fSuccess) { printf( " ConnectNamedPipe (%d)\n " , GetLastError()); return 0 ; } } // 分配内存 lpPipeInst = (LPPIPEINST) HeapAlloc(GetProcessHeap(), 0 , sizeof (PIPEINST)); if (lpPipeInst == NULL) { printf( " GlobalAlloc failed (%d)\n " , GetLastError()); return 0 ; } 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()); return 0 ; } } } return 0 ; } /* ************************************ * 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()); return 0 ; } // 连接到新的客户端 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()); return 0 ; } switch (GetLastError()) { // overlapped连接进行中. case ERROR_IO_PENDING: fPendingIO = TRUE; break ; // 已经连接,因此Event未置位 case ERROR_PIPE_CONNECTED: if (SetEvent(lpo -> hEvent)) break ; // error default : { printf( " ConnectNamedPipe failed with %d.\n " , GetLastError()); return 0 ; } } 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 " ); return 0 ; } // 如果所有pipe实例都处于繁忙状态,等待2秒。 if ( ! WaitNamedPipe(lpszPipename, 2000 )) { printf( " Could not open pipe " ); return 0 ; } } // pipe已经连接,设置为消息读状态 dwMode = PIPE_READMODE_MESSAGE; fSuccess = SetNamedPipeHandleState( hPipe, // 句柄 & dwMode, // 新状态 NULL, // 不设置最大缓存 NULL); // 不设置最长时间 if ( ! fSuccess) { printf( " SetNamedPipeHandleState failed " ); return 0 ; } // 写入pipe fSuccess = WriteFile( hPipe, // 句柄 lpvMessage, // 写入的内容 (lstrlen(lpvMessage) + 1 ) * sizeof (TCHAR), // 写入内容的长度 & cbWritten, // 实际写的内容 NULL); // 非 overlapped if ( ! fSuccess) { printf( " WriteFile failed " ); return 0 ; } 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); return 0 ; }
3、I/O简介
I/O模式不仅在进程间通信时使用,任何具有数据流形式的输入输出(包括文件输入输出、内核通信、网络输入输出等)都涉及I/O模式。
异步( Asynchronous)和同步(Synchronous) I/O是两种基本的I/O模式。
同步I/O
所谓同步I/O是指在调用ReadFile、WriteFile等函数进行输入输出操作时,系统完成了输入输出ReadFile、WriteFile才返回。在操作系统进行I/O操作的过程上,用户态线程不能执行,因此在同步I/O时,如果需要在I/O时进行其他操作就只能再开启线程。
异步I/O
异步I/O是在调用ReadFile、WriteFile等函数后,函数立即返回,线程可以进行其他操作。剩下的I/O操作在系统内核中自动完成。那么在系统内核完成输入输出后,程序如何知道I/O是否已完成?
一种方法,称作完成函数(Completion Routines),如果使用ReadFileEx、WriteFileEx等进行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
转载于:https://my.oschina.net/u/2326085/blog/391277
Windows API 进程间通信,管道(Pipe)相关推荐
- #Linux#进程间通信# 管道(pipe)-标准流管道pipe
在#Linux#进程间通信# 管道(pipe)-普通管道pipe中,我们很容易可以看出普通管道一是单工,即只能单向传输,而标准流管道针对匿名管道PIPE一系列封装.返回文件流.只不过返回的文件流无法使 ...
- #Linux#进程间通信# 管道(pipe)-匿名管道pipe
我们通常把一个进程连接到另一个进程的一个数据流称为一个"管道",通常是用作把一个进程的输出通过管道连接到另一个进程的输入.管道本质上是内核的一块缓存.内核使用环形队列机制,借助内核 ...
- 【IPC第二个进程间通信】管道Pipe
IPC进程间通信+管道Pipe IPC(Inter-Process Communication,进程间通信). 管道用于进程间共享数据,事实上质是共享内存 ...
- Android 进程间通信之管道 - pipe
文章目录 Android 进程间通信专栏 linux支持的进程间通信 管道 - pipe 概要 配置环境 android 系统中使用管道的场景 Android 进程间通信专栏 linux支持的进程间通 ...
- linux操作系统进程间通信IPC之管道pipe及FIFO
linux环境下,各进程相互独立,如果想要交换两个进程之间的数据,需要通过内核,在内存中提供一个缓存区,一个进程往缓存区中写数据,一个往缓存区读数据,内核提供的这种机制称为进程间通信(IPC),常见的 ...
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...
- windows下进程间通信的(13种方法)
windows下进程通信方法 2.1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针 ...
- Windows下进程间通信及数据共享
进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码.数据以及它可利用的系统资源(如文件.管道等)组成. 多进程/多线程是Windows操作系统的一个基本特征.Microsoft W ...
- linux程序间管道通信,linux进程间通信——管道 详解
管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入.常说的管道多是指无名管道, 无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别. 有名管道叫nam ...
最新文章
- LeetCode刷题记录2——217. Contains Duplicate(easy)
- python基础学习1-计数器实例
- VS2005发布网站问题及aspnet_merge.exe”已退出,代码为 1的错误
- 简单的使用Seajs
- java包装项目_项目包装组织
- perclos嘴巴_一种基于视频分析的疲劳状态检测方法及装置与流程
- OpenShift 4 之AMQ Streams(1) - 多个Consumer从Partition接收数据
- Centos 6 搭建安装 Gitlab
- 关于使用stanfordcorenlp一直运行不报错的解决方法
- Javascript 事件入门
- 简单类型参数是值传递,对象参数是引用传递
- flash发布html快捷键,Flash菜单操作之快捷键
- pip下载太慢?聪明人都这样
- 部署dicuz论坛网站
- 从零搭建一辆ROS小车
- Python办公——三行代码拆分表格
- 如何使用CorelDRAW 2019快速制作幻影图像效果
- 2020年最好的机器人学仿真工具软件汇总
- Linux命令curl详解(一)
- 在html循环字母,怎么样能遍历一个字符串
热门文章
- 连续播放20分钟出现‘休息弹窗‘ 解决方案
- Javascript系列(九):类数组和数组
- 数据挖掘xgb使用总结
- Web安全学习思维导图,[web入门菜鸡萌新必备]
- python websocket_python 模拟websocket通信
- Jaccard index, 又称为Jaccard相似系数 (错误处已备注)
- php 做订餐系统,做个订餐系统,需要的工作量有多大?
- unity-真机调试Android
- 亿方云面试经验(后台开发工程师实习)
- Oracle中视图DDL查询