欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > WindowsAPI 查阅笔记:进程间管道通信

WindowsAPI 查阅笔记:进程间管道通信

2024/10/24 12:30:51 来源:https://blog.csdn.net/orange1710/article/details/141163764  浏览:    关键词:WindowsAPI 查阅笔记:进程间管道通信

进程间有名管道的通信:

1.1 重叠I/O(Overlapped I/O)

重叠I/O(Overlapped I/O)是Windows编程中的一种异步 I / O 处理方式,它允许程序在发出I/O请求后继续执行其他任务,而不必等待I/O操作完成。这种机制通过使用OVERLAPPED结构体和相关的异步I/O函数(如ReadFileEx、WriteFileEx等)来实现。

在重叠I/O中,OVERLAPPED结构体用于存储I/O操作的上下文信息。它包含以下成员:

typedef struct _OVERLAPPED {ULONG_PTR   Internal;		//一个指向内部缓冲区的指针。ULONG_PTR   InternalHigh;	//内部缓冲区的高位地址。HANDLE      hEvent;			//一个用于异步操作的事件对象。
} OVERLAPPED, *LPOVERLAPPED;

当程序发出一个异步I/O请求时,它会将OVERLAPPED结构体的地址作为参数传递给异步I/O函数。异步I/O函数会在后台执行I/O操作,并在操作完成时通过设置事件对象或完成端口来通知程序。程序可以在任何时间点检查事件对象或完成端口的状态,以确定I/O操作是否已完成,并处理操作的结果。

重叠I/O通常用于需要高性能和响应能力的应用程序,如数据库服务器、文件服务器等。它允许这些应用程序在等待I/O操作完成的同时处理其他任务,从而提高系统的吞吐量和整体性能。

请注意,使用重叠I/O需要小心处理错误和资源管理。程序必须确保在异步I/O操作完成后正确地关闭事件对象和句柄,以避免资源泄漏和其他潜在问题。此外,程序还需要正确处理异步I/O操作的结果,包括检查错误代码和处理读取或写入的数据。

1.2 异步 I/O

同步 I / O ,指的是调用 ReadFile、WriteFile 等函数进行输入输出时候,系统完全执行该函数后才继续执行接下来的代码。
异步 I / O ,指的是调用 ReadFile、WriteFile 等函数之后,函数立即返回,线程可以进行其他操作。剩下的I/O操作再系统内核中自动完成。
再系统内核完成出入输出后,程序如何知道I/O已经完成:通过【完全函数】

1.3 完全函数

【完全函数】,如果使用 ReadFileEx、WriteFileEx等进行 I/O ,可以指定完全函数,所谓【完全函数】指的是内核完成 I/O 之后,内核会自动回调这个函数。当【完全函数】被调用时候,就指明内核已经完成了 I/O,程序可以再这个函数中进行一个 I/O 完成后需要的操作(例如释放内存)。

1.3 代码

  • 服务端创建了一个异步I/O管道,

  • 服务端 CompletedWriteRoutine 和 CompletedReadRoutine 两个函数互为对方的【完全代码】

  • 服务端 一开始创建了,一个【事件】,客户端连接成功后打开,否则等待。

  • 客户端通过管道名连接有名管道。

服务端:

#include <windows.h>
#include <cstdio>
#include <tchar.h>
#include <stdlib.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;/***********
pipe 通信服务端主函数 
***********/ 
int main(void)
{HANDLE hConnectEvent;OVERLAPPED oConnect;LPPIPEINST lpPipeInst;DWORD dwWait, cbRet;BOOL fSuccess, fPendingIO;//用于连接操作的事件对象hConnectEvent = CreateEvent(NULL,	//默认属性TRUE,	//手工resetTRUE,	//初始状态 signaledNULL	//未命名 );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://pendingif(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;//读和写,注意CompleteWriteRoutine 和 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;
}/*************************
建立连接实例
返回值 是否成功 
**********************/ 
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
//	LPTSTR lpszPipename = _T("\\\\.\\pipe\\samplenamedpipe");TCHAR lpszPipename[64] = TEXT("\\\\.\\pipe\\samplenamedpipe");//创建 named pipehPipe = CreateNamedPipe(lpszPipename,			//pipe名 PIPE_ACCESS_DUPLEX | 	//可读可写FILE_FLAG_OVERLAPPED,	//重叠I/O 模式//pipe模式PIPE_TYPE_MESSAGE |		//消息类型 pipePIPE_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)
{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;} //errordefault:{printf("ConnectNamePipe failed with %d.\n",GetLastError());return 0;}}return fPendingIO;
} /*************************
写入pipe操作的完成函数
当写操作完成时候被调用,开始读另一个请求 
*************************/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,//写操作完成后,调用CompleteReadRoutine(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); }if(! fRead){//出错 断开连接DisconnectAndClose(lpPipeInst); }return ;
}/*************************
读取pipe操作的完成函数
当写操作完成时候被调用,开始读另一个请求 
*************************/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,lpPipeInst->cbToWrite,(LPOVERLAPPED) lpPipeInst,//写入完成后,调用CompleteWriteRoutine(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine); }if(! fWrite){//出错 断开连接DisconnectAndClose(lpPipeInst); }return ;
}//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);return ;
} /********************
功能 断开一个连接的实例 
*******************/VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{//关闭连接实例if(! DisconnectNamedPipe(lpPipeInst->hPipeInst)){printf("DisconnectNamePipe failed with %d.\n",GetLastError()); } //关闭 pipe 实例的句柄CloseHandle(lpPipeInst->hPipeInst);//释放if(lpPipeInst != NULL){HeapFree(GetProcessHeap(), 0, lpPipeInst);} return ;
}

客户端:

//PipeClnt.cpp/**********************
通过 pipe 进行进程间通信 
*********************/#include <windows.h>
#include <cstdio>
#include <conio.h>
#include <tchar.h>//常量 
#define BUFSIZE 512/***********************
pipe 通信服务端主函数 
**********************/
int main(int argc, TCHAR *argv[])
{HANDLE hPipe;LPTSTR lpvMessage = TEXT("Default message from client");ll
//	TCHAR lpvMessage[64] = TEXT("Default message from client");TCHAR chBuf[BUFSIZE];BOOL fSuccess;DWORD cbRead, cbWritten, dwMode;
//	LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\samplenamedpipe");TCHAR lpszPipename[64] = TEXT("\\\\.\\pipe\\samplenamedpipe");if(argc > 1){//如果输入了参数,则使用输入的参数 lpvMessage = argv[1]; }while(1){//打开一个命名 pipehPipe = 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 实例都处于繁忙,等待2sif(!WaitNamedPipe(lpszPipename, 2000)){printf("Could not open pipe");return 0;} }//pipe 已经连接 设置为消息读状态dwMode = PIPE_READMODE_MESSAGE;fSuccess = SetNamedPipeHandleState(hPipe,	//句柄&dwMode,	//新状态NULL,	//不设置最大缓存NULL	//不设置最长时间 );if(!fSuccess){printf("SetNamePipeHandleState failed");return 0;}//写入 pipefSuccess = 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);//任意键退出 getch(); //关闭句柄CloseHandle(hPipe); return 0;
}

先启动服务端,再启动多个客户端。
运行结果:
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com