1、共享内存
使用CreateFileMapping函数创建文件映射,指定第一个参数为INVALID_HANDLE_VALUE表明在系统页文件中开辟共享内存,最后一个参数指定文件映射的名字。
使用MapViewOfFile将已创建的文件映射映射到当前进程地址空间,可以读写访问。
另一个进程可以使用OpenFileMapping通过制定文件映射名字,打开一个已经创建的文件映射。
使用MapViewOfFile将已创建的文件映射映射到当前进程地址空间,可以读写访问。
这样两个进程都可以讲共享内存映射到自己的进程地址空间,进行通信。
通信完毕后需要使用UnmapViewOfFile取消映射,CloseHandle销毁文件映射。
只适用于同一机器上的进程间通信。
写数据的进程:
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//创建文件映射,指向操作系统页面文件
HANDLE hFileMapping = ::CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(int), "TestMem");
//将共享内存映射到当前进程地址空间
int* addr = (int*)::MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, sizeof(int));
//向共享内存中写数据
*addr = 23;
getchar();
//解除共享内存到当前进程地址空间的映射
:: UnmapViewOfFile(addr);
//销毁文件映射
::CloseHandle(hFileMapping);
return 0;
}
读数据的进程:
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//打开一个已命名的文件映射
HANDLE hFileMapping = ::OpenFileMapping(FILE_MAP_READ, false, "TestMem");
//将其映射到进程地址空间
int* addr = (int*)::MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, sizeof(int));
//读取共享内存中的数据
cout<<*addr<<endl;
getchar();
//解除到进程地址空间的映射
:: UnmapViewOfFile(addr);
//关闭文件映射
::CloseHandle(hFileMapping);
return 0;
}
2、命名管道
服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。
对一个客户机应用来说,它只能同一个已命名的管道服务器建立连接。
在客户机和服务器之间,一旦建好连接,两个进程都能对标准的Win32函数,在管道上进行数据的读取与写入。这些函数包括ReadFile和WriteFile等等。
命名管道的命名格式:
\\ServerName\Pipe\PipeName其中ServerName是服务器名字,Pipe固定表示管道,PipeName是管道名称。
命名管道适用于单机或网络进程间通信。
服务器端
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//创建一个命名管道
HANDLE hPipe = ::CreateNamedPipe("\\\\.\\pipe\\MyPipe", PIPE_ACCESS_DUPLEX, 0, 2, 0, 0, 0, NULL);
//将服务器端连接到命名管道,并监听是否有客户端连接到已创建的命名管道,若有则返回
::ConnectNamedPipe(hPipe, NULL);
//从管道中读数据
char cBuffer[1024] = {0};
DWORD dwSize = 0;
::ReadFile(hPipe, cBuffer, 1024, &dwSize, NULL);
cout<<cBuffer<<endl;
//断开服务器端与命名管道的连接
::DisconnectNamedPipe(hPipe);
//销毁管道
::CloseHandle(hPipe);
return 0;
}
客户端
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//客户端等待,直到指定的命名管道可用
::WaitNamedPipe(L"\\\\.\\pipe\\MyPipe", NMPWAIT_WAIT_FOREVER);
//创建一个命名管道的实例
HANDLE hPipe = ::CreateFile(L"\\\\.\\pipe\\MyPipe", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//写数据
const char* cWrite = "test pipe";
DWORD dwSize = 0;
::WriteFile(hPipe, cWrite, (DWORD)strlen(cWrite)+1, &dwSize, NULL);
//关闭管道
::CloseHandle(hPipe);
return 0;
}
3、邮件槽
邮件槽(Mailslots)提供进程间单向通信能力,服务器端创建邮件槽,客户端通过名字连接邮件槽,同一个邮件槽只允许客户端向服务器端发送数据。
邮件槽命名与命名管道类似。
支持单机或不同计算机之间的进程间通信。
服务器端
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//创建一个邮件槽
//第三个参数若为0,ReadFile即使读不到数据也立刻返回;若为MAILSLOT_WAIT_FOREVER,ReadFile将等待知道读到数据
HANDLE hMail = ::CreateMailslot("\\\\.\\mailslot\\MyMail", 0, MAILSLOT_WAIT_FOREVER, NULL);
//从邮件槽中读数据
char cBuffer[1024] = {0};
DWORD dwSize = 0;
::ReadFile(hMail, cBuffer, 1024, &dwSize, NULL);
cout<<cBuffer<<endl;
//销毁邮件槽
::CloseHandle(hMail);
return 0;
}
客户端
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
int main()
{
//打开一个已命名的邮件槽
//第二个参数必须设为GENERIC_WRITE,因为客户机只能向服务器写入数据。
//第三个参数必须设为FILE_SHARE_READ,允许服务器在邮槽上打开和进行读操作。
HANDLE hMail = ::CreateFile(L"\\\\.\\mailslot\\MyMail", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//写数据
const char* cWrite = "test mail";
DWORD dwSize = 0;
::WriteFile(hMail, cWrite, (DWORD)strlen(cWrite)+1, &dwSize, NULL);
//关闭邮件槽
::CloseHandle(hMail);
return 0;
}
4、socket连接
服务器端和客户端通过socket连接通信,适用于网络间进程通信。
服务器端
#include <iostream>
#include <winsock.h>
#pragma comment (lib, "Ws2_32.lib")
using namespace std;
int main()
{
//初始化使用socket函数要用到的dll
WSADATA wd;
::WSAStartup(MAKEWORD(2,2), &wd);
//本机地址信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(1555);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//创建套接字,并与本机地址绑定
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0);
::bind(s, (const sockaddr*)&addr, sizeof(addr));
//监听该套接字,准备接收连接
::listen(s, 2);
//等待客户端连接,若客户端连接则返回,否则一直等待
//返回结果是用来通信的新套接字
sockaddr_in client_addr;
SOCKET ns = ::accept(s, (sockaddr*)&client_addr, NULL);
//在新套接字上收数据
while(1)
{
char cBuffer[1024] = {0};
::recv(ns, cBuffer, 1024, 0);
cout<<cBuffer<<endl;
}
//关闭套接字
::closesocket(ns);
::closesocket(s);
//ws2.dll的收尾工作
::WSACleanup();
return 0;
}
客户端
#include <iostream>
#include <winsock.h>
#pragma comment (lib, "Ws2_32.lib")
using namespace std;
int main()
{
//初始化使用socket函数要用到的dll
WSADATA wd;
WSAStartup(MAKEWORD(2,2), &wd);
//服务器地址信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(1555);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//创建套接字,并与服务器连接
SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0);
::connect(s, (const sockaddr*)&addr, sizeof(addr));
//发送数据
while(1)
{
char cData[1024] = {0};
cin>>cData;
::send(s, cData, (int)strlen(cData)+1, 0);
}
//关闭套接字
::closesocket(s);
//ws2.dll的收尾工作
::WSACleanup();
return 0;
}
5、dll共享数据段
动态链接库被加载后,映射到各自进程地址空间,但位于共享数据段内的变量为所有加载该dll的进程所共享。
把需要共享的数据变量放入自定义的数据段中, 需要初始化的变量放入 #pragma data_seg()定义的数据段中;不需初始化的变量放入#pragma bss_seg()定义的数据段中。
然后打开/SECTION 开关,请连接器为这些数据段定义共享属性。
dll文件
//共享数据段
#pragma data_seg("Shared")
__declspec(dllexport) char g_SharedBuffer[1024] = {0};
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")
//非共享数据段
__declspec(dllexport) char g_Buffer[1024] = {0};
进程1写数据
#include <iostream>
#pragma comment (lib, "..\\..\\dll\\release\\dll")
using namespace std;
int main()
{
//导入dll中的变量
__declspec(dllimport) char g_Buffer[];
__declspec(dllimport) char g_SharedBuffer[];
//分别向两个变量中写入数据
strcpy(g_Buffer, "not shared");
strcpy(g_SharedBuffer, "shared");
cout<<g_Buffer<<endl;
cout<<g_SharedBuffer<<endl;
getchar();
return 0;
}
进程2读数据
#include <iostream>
#pragma comment (lib, "..\\..\\dll\\release\\dll")
using namespace std;
int main()
{
//导入dll中的变量
__declspec(dllimport) char g_Buffer[];
__declspec(dllimport) char g_SharedBuffer[];
//输出结果
cout<<g_Buffer<<endl; //非共享数据段内的变量没有进程1写的值
cout<<g_SharedBuffer<<endl; //共享数据段内的变量读出了进程1写的值
getchar();
return 0;
}
6、WM_COPYDATA消息
发送方只需使用调用SendMessage函数,参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。
接收方只需像处理其它消息那样处理WM_COPY DATA消息,这样收发双方就实现了数据共享。
7、剪切板
8、匿名管道
在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。
通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写端点句柄,然后实现通信。