关闭

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

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

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

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

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

一、注意点

1、常用API

Pipes[2]

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

2、示例

1)服务器端

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

CreateNamedPipe

ConnectNamedPipe

ReadFile/WriteFile

DisconnectNamedPipe

示例代码

通过pipe进程间通信-服务器端
通过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; // error default: { 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进程间通信-客户端
通过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

http://www.cnblogs.com/mydomain/archive/2011/01/10/1931914.html
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1674136次
    • 积分:19304
    • 等级:
    • 排名:第442名
    • 原创:27篇
    • 转载:1525篇
    • 译文:2篇
    • 评论:128条
    最新评论