管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。
管道分为匿名管道和命名管道。
匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。
命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。
管道客户端代码实现如下
/* 头文件 */ #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; }
管道服务端代码实现如下,
/* 头文件 */ #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); }