进程间通信方式
进程间通信(Interprocess Communication, IPC)是指不同的进程之间进行数据共享和数据交换。本文是转载的这篇文章,详细内容可以查看这篇文章
1 共享内存
Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。
进程在创建文件映射对象时0xFFFFFFFF(INVALID_HANDLE_VALUE)来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。
通信流程:
服务器端: 创建文件映射内核对象->将文件数据映射到本进程的地址空间 -> 将数据写入到内存中->从进程的地址空间中撤消文件数据的映像 -> 关闭文件映射对象和文件对象
客户端:打开命名的文件映射内核对象 -> 将文件映射内核对象hFileMapping映射到当前应用程序的进程地址pBuf,通过该指针可以读写共享的内存区域 -> 读取数据->从进程的地址空间中撤消文件数据的映像 -> 关闭文件映射对象和文件对象
server
TCHAR szMapFileName[] = TEXT("MyFileMappingName"); //映射文件名,即共享内存的名称
CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUFFER_SIZE, szMapFileName); //INVALID_HANDLE_VALUE表示创建一个进程间共享的对象
(LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE);//将文件对象数据映射到进程的地址空间
CopyMemory((void*)pBuf, szSendData, _tcslen(szSendData) * sizeof(TCHAR)); //将要传输的数据写入到内存中
UnmapViewOfFile(pBuf);// 从进程的地址空间中撤消文件数据的映像
CloseHandle(hMapFile);// 关闭文件映射对象和文件对象
client
OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szMapFileName);//打开在server创建的文件对象,即共享内存
(LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE);//将本进程的地址指到共享内存的空间上
//读取数据就是简单的将读取char* 类型的字符串
UnmapViewOfFile(pBuf);// 从进程的地址空间中撤消文件数据的映像
CloseHandle(hMapFile);// 关闭文件映射对象和文件对象
代码神马的就不贴了,想用的话可以参考上面大神的代码。
个人理解:共享内存其实就是将内核中一部分公共内存(大家都可以访问到的区域)存放数据,然后不同的进程可以直接进去(客户端和服务端的同一个文件对象)拿数据。
2 管道
管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的-管道的两端点既可读也可写。
(1) 匿名管道(Anonymous Pipe)是在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。
通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。
匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。
匿名管道通信过程:
>>父进程读写过程
①创建匿名管道
②创建子进程,并对子进程相关数据进行初始化(用匿名管道的读取/写入句柄赋值给子进程的输入/输出句柄)。
③关闭子进程相关句柄。(进程句柄,主线程句柄)
④对管道读写
>>子进程读写过程
①获得输入输出句柄
②对管道读写
(2) 命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。
命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。
命名管道有两种通信方式:
A. 字节模式:在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动(PIPE_TYPE_BYTE 数据在通过管道发送时作为字节流发送,不能与PIPE_READMODE_MESSAGE共用)
B. 消息模式:在消息模式下,客户机和服务器则通过一系列不连续的数据单位,进行数据收发,每次在管道上发一条消息后,它必须作为一条完整的消息读入(PIPE_TYPE_MESSAGE 数据在通过管道发送时作为消息发送,不能与PIPE_READMODE_BYTE共用)
通信流程:
服务器端: 创建命名管道->创建事件对象 ->创建管道连接-> 等待用户连接 -> 读写数据
客户端:连接命名管道 -> 打开命名管道 -> 读写数据
函数流程:
服务器端:CreateNamedPipe-> CreateEvent -> ConnectNamedPipe->WaitForSingleObject(事件对象,等待时间) -> ReadFile/WriteFile
客户端:WaitNamedPipe(L"\\.\pipe\Communication",等待时间) -> CreateFile -> ReadFile/WriteFile
注:等待时间单位为ms。
server
CreateNamedPipe(L"\\\\.\\pipe\\Communication", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
0, 1, 1024, 1024, 0, NULL);
CreateEvent(NULL, TRUE, FALSE, NULL);//创建事件对象
ConnectNamedPipe(hPipe, &ovlap);//创建管道连接
WaitForSingleObject(hEvent,2000) //等待链接 2000ms INFINITE:无限等待
CloseHandle(hEvent);//关闭事件,我认为这里已经连接上了,就不需要了
ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL);
WriteFile(hPipe, senbuf, (DWORD)strlen(senbuf) + 1, &dwWriteLen, NULL);
client
WaitNamedPipe(L"\\\\.\\pipe\\Communication", NMPWAIT_WAIT_FOREVER);//这里还是无限等待
CreateFile(L"\\\\.\\pipe\\Communication", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);//打开连接管道
WriteFile(hPipe, senbuf, strlen(senbuf) + 1, &dwWriteLen, NULL);
ReadFile(hPipe, rebuf, 100, &dwReadLen, NULL);
代码神马的就不贴了,想用的话可以参考上面大神的代码。
个人感觉命名管道应该会用到类似socket通信这种东西,和共享内存的机制应噶是不一样的,因为命名管道是可以在不同的计算机上通信的。现在只是简单的了解,等以后碰到了再来更新。
3 邮件槽
通信流程:
服务器端: 创建邮槽对象 -> 读取数据 -> 关闭邮槽对象
客户端:打开邮槽对象 -> 写入数据 -> 关闭邮槽对象
注意:
(1)邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。
(2)邮槽可以实现一对多的单向通信,我们可以利用这个特点编写一个网络会议通知系统,而且实现这一的系统所需要编写的代码非常少.如果读者是项目经理,就可以给你手下每一位员工的机器上安装上这个系统中的邮槽服务器端程序,在你自己的机器上安装油槽的客户端程序,这样,当你想通知员工开会,就可以通过自己安装的邮槽客户端程序.将开会这个消息发送出去,因为机器上都安装了邮槽服务器端的程序,所以他们都能同时收到你发出的会议通知.采用邮槽实现这一的程序非常简单的,如果采用Sockets来实现这一的通信,代码会比较复杂。
(3)邮槽是一种单向通信机制,创建邮槽的服务器进程只能读取数据,打开邮槽的客户机进程只能写入数据。
(4)为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。
4 剪贴板
剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息,为Windows应用程序之间进行数据共享提供了一个中介,Windows已建立的剪切(复制)-粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时,应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式。
剪贴板是一个非常松散的交换媒介,可以支持任何数据格式,每一格式由一无符号整数标识,对标准(预定义)剪贴板格式,该值是Win32 API定义的常量;对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用,不能在网络上使用。
剪贴板(ClipBoard)是内存中的一块区域,是Windows内置的一个非常有用的工具,通过小小的剪贴板,架起了一座彩桥,使得在各种应用程序之间,传递和共享信息成为可能。然而美中不足的是,剪贴板只能保留一份数据,每当新的数据传入,旧的便会被覆盖。
通信流程:
发送数据:
(1)打开剪贴板
(2)清空并占据剪贴板
(3)向剪贴板放入数据:
①获得一块堆内存GlobalAlloc
②锁定堆内存GlobalLock并获得堆内存首地址
③向剪贴板放入数据SetClipboardData
④释放堆内存GlobalUnlock
(4)关闭剪贴板
接收数据:
(1)打开剪贴板
(2)检查剪贴板中的数据格式
(3)从剪贴板中获取数据:
①接受数据GetClipboardData
②用GlobalLock获得地址并锁定内存
③用GlobalUnlock解除锁定内存
(4)关闭剪贴板
哈哈哈,经常用的剪贴板没有想到吧,生活中处处都是技术,我们要善于去发现它。
5 Sockets
Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外,还扩展了一组针对Windows的函数,使程序员可以充分利用Windows的消息机制进行编程。
现在通过Sockets实现进程通信的网络应用越来越多,这主要的原因是Sockets的跨平台性要比其它IPC机制好得多,另外WinSock 2.0不仅支持TCP/IP协议,而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作,这使得在单机的进程间进行简单数据传递不太方便,这时使用下面将介绍的WM_COPYDATA消息将更合适些。
以上是将前辈们的东西转移过来,也方便自己以后回顾学习,原文地址。