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

news2024/11/25 12:28:53

进程间有名管道的通信:

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,	//手工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;
				//读和写,注意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 pipe
	hPipe = CreateNamedPipe(
		lpszPipename,			//pipe名 
		
		PIPE_ACCESS_DUPLEX | 	//可读可写
		FILE_FLAG_OVERLAPPED,	//重叠I/O 模式
		//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)
{
	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("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){
		
		//打开一个命名 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 实例都处于繁忙,等待2s
		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("SetNamePipeHandleState 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);
	
	//任意键退出 
	getch(); 
	//关闭句柄
	CloseHandle(hPipe); 
	return 0;
}
 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2035886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

萌啦定价工具,萌啦数据ozon定价工具

在电商行业日益竞争激烈的今天&#xff0c;精准定价成为了商家们获取市场竞争优势的关键一环。尤其是对于在Ozon平台上耕耘的卖家而言&#xff0c;无论是本土卖家还是跨境商家&#xff0c;如何快速、准确地制定出既符合市场需求又能保障利润的价格策略&#xff0c;成为了亟待解…

高防服务器的机制和原理

高防服务器是一种具备强大防御能力的服务器&#xff0c;旨在保护网站免受各种网络攻击&#xff0c;如DDoS&#xff08;分布式拒绝服务&#xff09;攻击、CC&#xff08;ChallengeCollapsar&#xff09;攻击等。今天小编将从流量过滤与清洗、负载均衡与反向代理、实时监控与报警…

圈内水刊“三巨头”之首实至名归?发文量飙升至9000+,硕博小白照样发1区TOP!

【SciencePub学术】昨天&#xff0c;小编给大家介绍了环境水刊“三巨头”之一的《Journal of Hazardous Materials》&#xff0c;本期&#xff0c;给大家带来的是位于环境水刊“三巨头”之首的《Science of the Total Environment》&#xff0c;属于JCR1区中科院1区TOP&#xf…

冷数据归档(历史库),成本与性能如何兼得?| OceanBase应用实践

随着数据量的迅猛增长&#xff0c;企业和组织在数据库管理方面遭遇的挑战愈发凸显。数据库性能逐渐下滑、存储成本节节攀升&#xff0c;以及数据运维复杂性的增加&#xff0c;这些挑战使得DBA和开发者在数据管理上面临更大的压力。 为了应对这些挑战&#xff0c;对数据生命周期…

vulnstack-5

环境搭建 靶场虚拟机共用两个&#xff0c;一个外网一个内网&#xff0c;用来练习红队相关内容和方向&#xff0c;主要包括常规信息收集、Web攻防、代码审计、漏洞利用、内网渗透以及域渗透等相关内容学习。 虚拟机密码 win7 sun\heart 123.com sun\Administrator dc123.com # …

华为软件测试笔试真题,赶快收藏

软件测试工程师笔试题目 一&#xff0e;填空 1、 系统测试使用&#xff08; C &#xff09;技术, 主要测试被测应用的高级互操作性需求, 而无需考虑被测试应用的内部结构。 A、 单元测试 B、 集成测试 C、 黑盒测试 D、白盒测试 2、单元测试主要的测试技术不包括&#xff08;…

【nvidia-smi】Failed to initialize NVML: Driver/library version mismatch

服务器更新后&#xff0c;输入nvidia-smi出现如下报错&#xff1a; 解决方法参考&#xff1a; 已解决【nvidia-smi】Failed to initialize NVML: Driver/library version mismatch解决方法-腾讯云开发者社区-腾讯云 (tencent.com) 输入命令查看nvidia驱动的版本号&#xff1a…

Linux软件包yum

目录 Linux软件包管理器 yum关于rzsz注意事项查看软件包如何安装软件卸载命令 Linux开发工具Linux编辑器-vim使用1. vim的基本概念2. vim的基本操作3. vim正常模式命令集4. vim末行模式命令集5. vim操作总结 小彩蛋 Linux软件包管理器 yum 软件包 在Linux下安装软件&#xff…

java基础学习笔记2(8.9)

String equals比较堆里值 字符串比较用str1.equals(str2)&#xff1b; 比较栈里的值 JDK7以后字符串常量池进入了堆里面。 在Java中&#xff0c;StringBuffer 和 StringBuilder 是用于创建可变字符串的类。它们提供了比 String 更高效的字符串操作&#xff0c;尤其是在需要…

ICM-20948芯片详解(13)

接前一篇文章&#xff1a;ICM-20948芯片详解&#xff08;12&#xff09; 六、寄存器详解 2. USER BANK 0寄存器详述 &#xff08;60&#xff09;FIFO_COUNTH 高5位&#xff0c;计数表示FIFO中写入的字节数。 &#xff08;61&#xff09;FIFO_COUNTL 低8位&#xff0c;计数表…

IMAX ENHANCED认证的护眼三色激光投影仪,选极米 RS 10 Pro把专业IMAX影院带回家

对于追求大屏体验的用户来说&#xff0c;智能投影仪有着电视机无法比拟的优势。因此&#xff0c;智能投影仪如今也逐步替代传统电视机&#xff0c;成为了许多家庭的必备家电之一。对于当代年轻人而言&#xff0c;无论是追剧、看电影还是打游戏&#xff0c;大屏幕都始终比传统电…

西部数据拒绝2.62亿美元巨额赔偿:硬盘专利侵权案将上诉

西部数据&#xff08;Western Digital&#xff0c;简称WD&#xff09;被加州的一个陪审团裁定需支付2.62亿美元的赔偿金&#xff0c;原因是该公司侵犯了德国科学家Dieter Suess拥有的硬盘驱动器&#xff08;HDD&#xff09;记录技术专利。Suess是维也纳大学功能性材料物理学教授…

linux系统编程:进程(2)

1.在fork函数前打开函数和fork之后打开文件区别 1.fork之前open 子进程会继承父进程已打开的文件的相关信息所以&#xff0c;此时父子进程 会影响统一个offset值 2.fork之后open 父子进程各自有各自的打开文件的信息:相互之间不会有影响。 2.进程创建好之后: 1.任务--- …

Java流程控制01:用户交互Scanner

本节教学视频链接&#xff1a;https://www.bilibili.com/video/BV12J41137hu?p33&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV12J41137hu?p33&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 Scanner 类用于扫描输入文本从字符串中提…

具备长短距离注意力模块的Lite Transformer

Transformer在自然语言处理(例如&#xff0c;机器翻译、问答)中已经变得无处不在;然而&#xff0c;它需要大量的计算才能实现高性能&#xff0c;这使得它不适合受硬件资源和电池严格限制的移动应用程序。在本文中&#xff0c;提出了一个高效的移动NLP架构&#xff0c;Lite Tran…

jq8900-16p与stm32f103c8t6测试

jq8900-16p与stm32f103c8t6测试 引言 本文从购买器件, 到连线步骤, 再到驱动的模块原理讲解, 后面根据不同的语音, 进行文字转语音步骤,全在资料导航里面. 本模块后面着重讲解, jq8900快速移植 本文资料导航 模块购买步骤 跳转 连线步骤 跳转 https://blog.csdn.net/qq_57484…

Vue的事件处理、事件修饰符、键盘事件

目录 1. 事件处理基本使用2. 事件修饰符3. 键盘事件 1. 事件处理基本使用 使用v-on:xxx或xxx绑定事件&#xff0c;其中xxx是事件名&#xff0c;比如clickmethods中配置的函数&#xff0c;都是被Vue所管理的函数&#xff0c;this的指向是vm或组件实例对象 <!DOCTYPE html&g…

探秘未来驾驶,汽车智能座舱软件测试的艺术与科学

什么是智能座舱软件测试 智能座舱软件测试是一种专门针对现代汽车中集成的先进驾驶辅助系统(ADAS)、信息娱乐系统、人机交互界面(HMI)以及其他智能座舱组件的软件质量保证过程。随着汽车行业的数字化和智能化转型&#xff0c;无人驾驶的出现&#xff0c;智能座舱已成为汽车用户…

Keepalived+Haproxy实现高可用

keepalived利用 VRRP Script 技术&#xff0c;可以调用外部的辅助脚本进行资源监控&#xff0c;并根据监控的结果实现优先 动态调整&#xff0c;从而实现其它应用的高可用性功能。 一、VRRP Script 配置 1、定义脚本 vrrp_script&#xff1a;自定义资源监控脚本&#xff0c;…

【光流估计】【深度学习】Windows11下FastFlowNet代码Pytorch官方实现与源码讲解

【光流估计】【深度学习】Windows11下FastFlowNet代码Pytorch官方实现与源码讲解 提示:最近开始在【光流估计】方面进行研究,记录相关知识点,分享学习中遇到的问题已经解决的方法。 文章目录 【光流估计】【深度学习】Windows11下FastFlowNet代码Pytorch官方实现与源码讲解前言…