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)相关推荐

  1. #Linux#进程间通信# 管道(pipe)-标准流管道pipe

    在#Linux#进程间通信# 管道(pipe)-普通管道pipe中,我们很容易可以看出普通管道一是单工,即只能单向传输,而标准流管道针对匿名管道PIPE一系列封装.返回文件流.只不过返回的文件流无法使 ...

  2. #Linux#进程间通信# 管道(pipe)-匿名管道pipe

    我们通常把一个进程连接到另一个进程的一个数据流称为一个"管道",通常是用作把一个进程的输出通过管道连接到另一个进程的输入.管道本质上是内核的一块缓存.内核使用环形队列机制,借助内核 ...

  3. 【IPC第二个进程间通信】管道Pipe

    IPC进程间通信+管道Pipe                IPC(Inter-Process Communication,进程间通信).         管道用于进程间共享数据,事实上质是共享内存 ...

  4. Android 进程间通信之管道 - pipe

    文章目录 Android 进程间通信专栏 linux支持的进程间通信 管道 - pipe 概要 配置环境 android 系统中使用管道的场景 Android 进程间通信专栏 linux支持的进程间通 ...

  5. linux操作系统进程间通信IPC之管道pipe及FIFO

    linux环境下,各进程相互独立,如果想要交换两个进程之间的数据,需要通过内核,在内存中提供一个缓存区,一个进程往缓存区中写数据,一个往缓存区读数据,内核提供的这种机制称为进程间通信(IPC),常见的 ...

  6. Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

  7. windows下进程间通信的(13种方法)

    windows下进程通信方法 2.1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针 ...

  8. Windows下进程间通信及数据共享

    进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码.数据以及它可利用的系统资源(如文件.管道等)组成. 多进程/多线程是Windows操作系统的一个基本特征.Microsoft W ...

  9. linux程序间管道通信,linux进程间通信——管道 详解

    管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入.常说的管道多是指无名管道, 无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别. 有名管道叫nam ...

最新文章

  1. LeetCode刷题记录2——217. Contains Duplicate(easy)
  2. python基础学习1-计数器实例
  3. VS2005发布网站问题及aspnet_merge.exe”已退出,代码为 1的错误
  4. 简单的使用Seajs
  5. java包装项目_项目包装组织
  6. perclos嘴巴_一种基于视频分析的疲劳状态检测方法及装置与流程
  7. OpenShift 4 之AMQ Streams(1) - 多个Consumer从Partition接收数据
  8. Centos 6 搭建安装 Gitlab
  9. 关于使用stanfordcorenlp一直运行不报错的解决方法
  10. Javascript 事件入门
  11. 简单类型参数是值传递,对象参数是引用传递
  12. flash发布html快捷键,Flash菜单操作之快捷键
  13. pip下载太慢?聪明人都这样
  14. 部署dicuz论坛网站
  15. 从零搭建一辆ROS小车
  16. Python办公——三行代码拆分表格
  17. 如何使用CorelDRAW 2019快速制作幻影图像效果
  18. 2020年最好的机器人学仿真工具软件汇总
  19. Linux命令curl详解(一)
  20. 在html循环字母,怎么样能遍历一个字符串

热门文章

  1. 连续播放20分钟出现‘休息弹窗‘ 解决方案
  2. Javascript系列(九):类数组和数组
  3. 数据挖掘xgb使用总结
  4. Web安全学习思维导图,[web入门菜鸡萌新必备]
  5. python websocket_python 模拟websocket通信
  6. Jaccard index, 又称为Jaccard相似系数 (错误处已备注)
  7. php 做订餐系统,做个订餐系统,需要的工作量有多大?
  8. unity-真机调试Android
  9. 亿方云面试经验(后台开发工程师实习)
  10. Oracle中视图DDL查询