C++ windows进程间通信
最近一直在找共享内存同步的操作,恰好这篇文章有讲解。本文转载:https://blog.csdn.net/bing_bing_bing_/article/details/82875302
方便记录,copy了一份。
2.进程间的通信
2.1进程
本章讲解windows平台下,进程间的通信方式。
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是操作系统动态执行的基本单元。简单的说,进程就是一段程序的执行过程。
进程和线程:进程是系统动态执行的基本单位,也是系统分配资源的基本单位;线程是进程中执行的最小单位,它可以访问进程的共享资源。
进程之间对共享内存等进行读写操作,需要使用互斥机制,常使用Mutex;进程的同步机制包括Event、Semaphore,常使用Semaphore。
进程间的通信不仅包括进程的同步互斥,还包括进程间数据的传输。
进程间常用的通信方式:共享内存、管道、信号量、端口。其中Mutex和Event放在共享内存中讲解,端口会在网络编程那章讲解。
2.2 Shared Memory
Shared Memory称为共享内存,它是进程间数据传输最快的通信方式。由于共享内存是所有进程都可以访问,因此共享内存的操作需要加锁。
现在实现进程A和进程B,进程A对共享内存写数据,进程B对共享内存读数据。
//进程A
#include <iostream>
#include <Windows.h>
using namespace std;
#define BUF_SIZE 4096
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc,char ** argv)
{
//步骤1:创建共享文件句柄
HANDLE shared_file = CreateFileMapping(
INVALID_HANDLE_VALUE,//物理文件句柄
NULL, //默认安全级别
PAGE_READWRITE, //PAGE_READWRITE表示可读可写,PAGE_READONLY表示只读,PAGE_WRITECOPY表示只写
0, //高位文件大小
BUF_SIZE, //低位文件大小
L"ShareMemory" //共享内存名称
);
if (shared_file == NULL)
{
cout<<"Could not create file mapping object..."<<endl;
return 1;
}
//步骤2:映射缓存区视图,得到指向共享内存的指针
LPVOID lpBUF = MapViewOfFile(
shared_file, //已创建的文件映射对象句柄
FILE_MAP_ALL_ACCESS,//访问模式:可读写
0, //文件偏移的高32位
0, //文件偏移的低32位
BUF_SIZE //映射视图的大小
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = CreateMutex(NULL, FALSE, L"sm_mutex");//创建一个互斥器
H_Event = CreateEvent(NULL,//表示安全控制,一般为NULL
FALSE,//确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。
FALSE,//表示事件的初始状态,传入TRUE表示已触发
L"sm_event"//表示事件的名称,传入NULL表示匿名事件
);
//步骤3:操作共享内存
char Buffer[97];
while (1)
{
cout << "A proccess:Please input the content to the process B" << endl;
cin.getline(Buffer,97);
cout << "Buffer: " <<Buffer<< endl;
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥体加锁 获得互斥器的拥有权
memcpy(lpBUF, Buffer, strlen(Buffer)+1);
ReleaseMutex(H_Mutex); //放锁
SetEvent(H_Event);/激活等待的进程
}
CloseHandle(H_Mutex);
CloseHandle(H_Event);
//步骤4:解除映射和关闭句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
//进程B
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc, char ** argv)
{
//步骤1:打开共享文件句柄
HANDLE shared_file = OpenFileMapping(
FILE_MAP_ALL_ACCESS,//访问模式:可读写
FALSE,
L"ShareMemory" //共享内存名称
);
if (shared_file == NULL)
{
cout << "Could not open file mapping object..." << endl;
return 1;
}
//步骤2:映射缓存区视图,得到指向共享内存的指针
LPVOID lpBUF = MapViewOfFile(
shared_file, //已创建的文件映射对象句柄
FILE_MAP_ALL_ACCESS,//访问模式:可读写
0, //文件偏移的高32位
0, //文件偏移的低32位
0 //映射视图的大小,0表示从偏移量到文件映射的末尾,因为共享文件open端不知道其大小,所以写0
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"sm_mutex");
if (H_Mutex == NULL)
{
cout << "open mutex failed..." <<endl;
return 1;
}
H_Event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"sm_event");
if (H_Event == NULL)
{
cout << "open mutex failed..." << endl;
return 1;
}
char Buffer[97];
//步骤3:操作共享内存
while (1)
{
cout << "B proccess:Receive data from process A" << endl;
WaitForSingleObject(H_Event, INFINITE);
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥体加锁
memcpy(Buffer, lpBUF, strlen((char *)lpBUF) + 1);
ReleaseMutex(H_Mutex); //放锁
cout << Buffer << endl;
//system("PAUSE ");
}
CloseHandle(H_Event);
CloseHandle(H_Mutex);
//步骤4:解除映射和关闭句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
以管理员的身份运行命令提示符,先执行进程A的exe文件,再执行进程B的exe文件。
进程A运行结果:
A proccess:Please input the content to the process B
Hello China
Buffer: Hello China
A proccess:Please input the content to the process B
你好,中国!
Buffer: 你好,中国!
A proccess:Please input the content to the process B
进程B运行结果:
B proccess:Receive data from process A
Hello China
B proccess:Receive data from process A
你好,中国!
B proccess:Receive data from process A
2.3 PIPE
pipe称为管道,其分为命名管道、匿名管道。
2.3.1命名管道
//Server.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步骤1:定义管道名,点表示当前主机,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步骤2:创建命名管道
h_Mypipe = CreateNamedPipe(
MY_NAMED_PIPE, //为命名管道创建名称
PIPE_ACCESS_DUPLEX, //管道访问方式:PIPE_ACCESS_DUPLEX指双向模式
PIPE_TYPE_MESSAGE | //命名管道句柄的写入方式:以数据块的方式写入管道
PIPE_READMODE_MESSAGE | //命名管道句柄的读取方式:以数据块的方式从管道读取
PIPE_WAIT, //命名管道句柄的等待方式:阻塞方式
PIPE_UNLIMITED_INSTANCES, //管道所能创建的最大实例个数:1~255,
0, //管道的输出缓冲区容量,0表示默认大小
0, //管道的输入缓冲区容量,0表示默认大小 1000, //管道的默认等待超时,单位毫秒
NULL); //管道的安全性,NULL表示windows提供的默认安全
//INVALID_HANDLE_VALUE是CreateNamedPipe返回值,表示创建失败
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Create Named_Pipe Failed..." << endl;
return 1;
}
//步骤3:等待客户端的连接
if (!ConnectNamedPipe(h_Mypipe, NULL))
{
cout << "Connect Failed..." << endl;
return 1;
}
else
cout << "Connect Successed..." << endl;
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步骤4:读写管道
while (1)
{
//向客户端发送数据
cin.getline(szBuffer, BUF_SIZE);
cout << "服务器端发送数据:" << szBuffer<< endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout<<"服务器端发送成功:共"<< wLen<<"byte"<<endl;
//清除缓冲区
//memset(szBuffer, 0, BUF_SIZE);
//读取客户端数据
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "服务器接收客户端数据:" << szBuffer << ", 共" << rLen << "byte" << endl;
}
//步骤5:关闭管道
DisconnectNamedPipe(h_Mypipe);
CloseHandle(h_Mypipe);
return 0;
}
//Client.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步骤1:定义管道名,点表示当前主机,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步骤2:判断是否有可用的命名管道
//函数WaitNamedPipe:等待某个管道变成可用状态
//形参1:表示命名管道的名称
//形参2:NMPWAIT_USE_DEFAULT_WAIT使用管道创建时的默认超时设定;NMPWAIT_WAIT_FOREVER永远等待
if (!WaitNamedPipe(MY_NAMED_PIPE, NMPWAIT_USE_DEFAULT_WAIT))
{
cout << "No Named_Pipe Accessible..." << endl;
return 1;
}
else
cout << "Named_Pipe Accessible..." << endl;
//步骤3:打开指定命名管道
//函数CreateFile:创建或打开对象(这里对象指的是管道)
h_Mypipe = CreateFile(
MY_NAMED_PIPE, //创建或打开的对象(管道)名称
GENERIC_READ | //对象的访问方式:读访问
GENERIC_WRITE, //对象的访问方式:写访问
0, //对象是否共享:0表示不共享
NULL, //指向一个SECURITY_ATTRIBUTES结构的指针
OPEN_EXISTING, //对象的创建方式:OPEN_EXISTING表示打开对象(管道)
FILE_ATTRIBUTE_NORMAL, //设置对象的属性和标志
NULL);
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Open Named_Pipe Failed..." << endl;
return 1;
}
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步骤4:读写管道
while (1)
{
//读取服务器端数据
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "客户端接收服务器端数据:" << szBuffer << ", 共" << rLen << "byte" << endl;
//清除缓冲区
//memset(szBuffer, 0, BUF_SIZE);
//客户端发送数据
cin.getline(szBuffer, BUF_SIZE);
cout << "客户端发送数据:" << szBuffer << endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout << "客户端发送成功:共" << wLen << "byte" << endl;
}
//步骤5:关闭管道
CloseHandle(h_Mypipe);
return 0;
}
服务器端运行结果:
Connect Successed...
hello world
服务器端发送数据:hello world
服务器端发送成功:共12byte
服务器接收客户端数据:你好,中国, 共11byte
客户端运行结果:
Named_Pipe Accessible...
客户端接收服务器端数据:hello world, 共12byte
你好,中国
客户端发送数据:你好,中国
客户端发送成功:共11byte
命名管道可以实现进程之间的全双工通信。服务器端唯一有权创建管道,并等待客户端的连接请求;客户端只能使用已创建的管道,并打开管道与服务器端通信。服务器端和客户端都是可读可写的。
CreateNamedPipe(参数1,参数2,参数3,参数4,参数5,参数6,参数7,参数8)函数作用是创建命名管道。
参数1为命名管道创建名称。
参数2是命名管道的访问方式,PIPE_ACCESS_DUPLEX是双向模式,即服务器进程和客户端进程都可以从管道读写数据,PIPE_ACCESS_INBOUND是服务器只能从管道读数据,客户端只能向管道写数据,PIPE_ACCESS_OUTBOUND是服务器只能向管道写数据,客户端只能从管道读数据。
参数3分为3部分,分别是命名管道句柄的写入方式、命名管道句柄的读取方式、命名管道句柄的等待方式。命名管道句柄的写入方式分为PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE,PIPE_TYPE_BYTE是以字节流的形式写入管道,PIPE_TYPE_MESSAGE是以数据块(名为消息或报文)的形式写入管道。命名管道句柄的读取方式分为PIPE_READMODE_BYTE和PIPE_READMODE_MESSAGE,PIPE_READMODE_BYTE是以字节流的形式从管道读取数据,PIPE_READMODE_MESSAGE是以数据块(名为消息或报文)的形式从管道读取数据。在字节流模式中,数据以连续的字节在管道中传输,数据之间没有边界,适合大容量数据的传输;在数据块(消息)模式,数据以不连续的消息为基本单位在管道中传输,消息与消息之间有边界,适合小容量数据的传输。命名管道句柄的等待方式分为PIPE_WAIT和PIPE_NOWAIT,PIPE_WAIT是阻塞模式,PIPE_NOWAIT是非阻塞模式,这是对函数WaitNamedPipe()的设置,一般都设置为阻塞模式。
参数4是命名管道能创建的最大实例个数,范围是1~255。它的意思是有多少进程可以等待使用此命名管道,其中PIPE_UNLIMITED_INSTANCES表示255。
参数5是管道的输出缓冲区容量,0表示默认大小;也可以设为实际数据,如4096。
参数6是管道的输入缓冲区容量,0表示默认大小;也可以设为实际数据,如4096。
参数7是管道的默认等待超时,单位毫秒。这是对函数WaitNamedPipe()的等待时间设置。
参数8表示管道的安全性,NULL表示windows提供的默认安全。
返回值:执行成功,返回管道的句柄;执行失败,返回INVALID_HANDLE_VALUE。
WriteFile(参数1, 参数2, 参数3, 参数4, 参数5)函数是将数据写入一个文件或I/O设备。这里的作用是向管道写数据。
参数1是写入数据的文件句柄,这里指的是命名管道的句柄。
参数2是数据存放的缓冲区地址。
参数3是写入的数据长度,单位是字节。
参数4是实际写入管道的字节数。
参数5是指向OVERLAPPED结构体的指针,默认为NULL,表明使用默认的同步I/O方式。
ReadFile()同理,需要注意的是参数3不能使用strlen,因为数据还没读取出来,无法得知数据的大小,必须手动设置需要读取的数据长度或使用sizeof。
WaitNamedPipe(参数1,参数2)函数的作用是等待某个管道变成可用状态。
参数1是管道的名称。
参数2中NMPWAIT_USE_DEFAULT_WAIT使用管道创建时的默认超时设定,NMPWAIT_WAIT_FOREVER表示一直等待。
CreateFile(参数1, 参数2,参数3,参数4,参数5,参数6,参数7)函数的作用是打开或创建文件或I/O设备。
参数1是打开或创建的对象名称,这里指的是命名管道的名称。
参数2是对象的访问方式,GENERIC_READ是读访问,GENERIC_WRITE是写访问。
参数3表示对象是否共享,0表示不共享。
参数4是指向一个SECURITY_ATTRIBUTES结构的指针,不需要了解。
参数5是对象的打开或创建方式,OPEN_EXISTING表示打开已存在的对象。
参数6是设置对象的属性和标志,FILE_ATTRIBUTE_NORMAL表示该对象没有其他属性设置。
参数7是指定具有GENERIC_READ访问方式的模板文件的句柄,不需要了解。
注:以上函数都是windows平台提供的API。
C++ windows进程间通信的更多相关文章
- Windows进程间通信的各种方法
原文:Windows进程间通信的各种方法 进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码.数据以及它可利用的系统资源(如文件.管道等)组成.多进程/多线程是Windows操作系 ...
- [转]Windows进程间通信的各种方法
http://www.cnblogs.com/songQQ/archive/2009/06/03/1495764.html 道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包) ...
- windows进程间通信 .
摘 要: 随着人们对应用程序的要求越来越高,单进程应用在许多场合已不能满足人们的要求.编写多进程/多线程程序成为现代程序设计的一个重要特点,在多进程程序设计中,进程间的通信是不可避免的.Microso ...
- Windows进程间通信(下)
六.动态数据交换(Dynamic Data Exchange) 动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式.应用程序可以使用DDE进行一次性数据传输,也可以当出 ...
- Windows进程间通信(中)
二.文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容. W ...
- Windows进程间通信(上)
一.管道 管道(pipe)是用于进程间通信的共享内存区域.创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端.一个进程向管道写入信息,而另外一个进程从管道读取信息. 异步管道是基于字符 ...
- Windows进程间通信--命名管道
1 相关概述 命名管道(Named Pipes)是一种简单的进程间通信(IPC)机制.命名管道可以在同一台计算机的不同进程之间,或者跨越一个网络的不同计算机的不同进程之间的可靠的双向或单向的数据通信. ...
- Windows进程间通信--共享内存映射文件(FileMapping)--VS2012下发送和接收
之前以为两个互不相关的程序a.exe b.exe通信就只能通过网络,人家说可以通过发消息,我还深以为不然,对此,我表示万分惭愧. 之前课本上说的进程间通信,有共享内存.管道等之类的,但没有自己操刀写过 ...
- Windows进程间通信—命名管道
命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节.我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信.与Socket网络通信相比,命名管道不再需要编写身份验证的代码.将 ...
随机推荐
- 深入理解基于selenium的二次开发
对于做web端自动化测试的人来说,可能接触selenium比QTP还要多,但是我们在做基于selenium的二次开发的时候,经常会说到二次开发是为了易于维护,很多人可能不懂得维护的价值是什么,和到底要 ...
- 修改eclipse 代码字体大小以及文档字体大小
1..点击[window]在弹出的窗口中选择[preferences] 2.在弹出窗口中找到依次点击General(常规)——Apprearance(外观)——Colors and Fonts(颜色和 ...
- 文本操作 $(..).text() $(..).html() $(..).val()最后一种主要用于input
文本操作: $(..).text() # 获取文本内容 $(..).text('<a>1</a>') # 设置文本内容 $(..).html() $(..).html('< ...
- SC命令安装window服务
sc create Styler binpath= "D:\Styler\Styler.exe" start= auto displayname= "Styler&quo ...
- Incorrect string value: '\xF0\x9F\x98\x84\xF0\x9F
问题描述:从新浪微博抓取消息保存到MySQL数据中,对应数据库字段为varchar,字符编码utf-8.部分插入成功,部分插入失败,报错如标题. 在网上查询,有人说是编码问题,建议修改编码格式,比如改 ...
- javascript中所谓的“坑”收录
坑一: // 反例myname = "global"; // 全局变量function func() { alert(myname); // "undefined&quo ...
- sql的预编译问题
- node.js中实现同步操作的3种实现方法
这篇文章主要介绍了node.js中实现同步操作的3种实现方法,本文用实例讲解一些需要同步操作的情况下,如何编程实现,需要的朋友可以参考下 众所周知,异步是得天独厚的特点和优势,但同时在程序中同步的需求 ...
- IoC和DI的区别
------------------siwuxie095 IoC 和 DI 的区别 1.区别 (1)Io ...
- Java核心技术-泛型程序设计
使用泛型机制编写的代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性. 泛型对于集合类尤其有用 1 为什么要使用泛型程序设计 泛型程序设计意味着编写的代码可以 ...