IPC有以下方式:
1.文件映射(内存映射)
2.邮件槽
3.管道
4.剪切板
5.父子进程
6.网络TCP
1.文件映射(数据量大,本地进程间)
a.命名映射
A进程:打开文件,获得文件句柄,创建文件映射,获得映射句柄,将文件映射对象映射到当前地址空间,进行读写操作,卸载映射、关闭句柄
hFile = CreateFile()
hMapHandle = CreateFileMapping(hFile,MappingAttributes,flProtect,SizeHigh,SizeLow,lpName)
MapViewOfFile(hMapHandle,DesiredAccess,offset.HighPart,offset.LowPart, 0)
UnmapViewOfFile(pFilePtr)
CloseHandle(hFile and hMapHandle)
B进程:打开文件映射,将文件映射对象映射到当前地址空间,进行读写操作,卸载映射,关闭句柄
OpenFileMapping(dwDesiredAccess,bInheritHandle,Name)
MapViewOfFile(hMapFile, DesiredAccess, offset.HighPart, offset.LowPart, 0)
UnmapViewOfFile(p)
CloseHandle(hFile and hMapHandle)
b.匿名映射
A进程:创建匿名文件映射,得到映射句柄,将文件映射对象映射到当前地址空间,进行读写操作,卸载映射、关闭句柄
hMapHandle = CreateFileMapping(hFile,MappingAttributes,flProtect,SizeHigh,SizeLow,lpName)
MapViewOfFile(hMapHandle,DesiredAccess,offset.HighPart,offset.LowPart, 0)
UnmapViewOfFile(pFilePtr)
CloseHandle(hFile and hMapHandle)
B进程:输入A进程的进程ID和映射句柄,得到A进程句柄,将目标进程的映射句柄复制到当前进程,将文件映射对象映射到当前地址空间,进行读写操作,卸载映射、关闭句柄
DuplicateHandle(ProcessHandle, MappingHandle, GetCurrentProcess(), &v1, 0, FALSE, DUPLICATE_SAME_ACCESS);
VirtualAddress = (ULONG_PTR)MapViewOfFile(v1,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0)
UnmapViewOfFile(p)
CloseHandle(hFile and hMapHandle)
2.邮件槽(mailslot,服务端只能接收、客户端只能发送,利用主存的一个临时虚拟文件)
单向、广播、数据报
基于广播通信设计出来的,采用UDP数据传输协议
Server端(读消息)创建邮件槽,服务端获取邮件槽数据(邮件槽句柄、最大消息限制、下一条消息的大小、消息个数、无限时),根据消息个数遍历读取消息信息,关闭句柄
Handle Mailslot = CreateMailslot(mailslotName,MaxMessageSize)
GetMailslotInfo(m_MailSlotHandle, NULL, &NextSize, &MessageCount, NULL)
ReadFile(this->m_MailSlotHandle, BufferData, NextSize, &ReturnLength, NULL);
Close(Mailslot)
Client端(写消息)打开邮件槽,写入数据,关闭句柄
MailSlotHandle = CreateFile(MAIL_SLOT_NAME,GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
WriteFile(MailSlotHandle, BufferData, BufferLength*sizeof(TCHAR), &ReturnLength, NULL);
Close(Mailslot)
3.管道(匿名管道、命名管道)
a.匿名管道(本机父子进程,基于字符,半双工/单向,一般用于输入重定向)
父进程:创建匿名管道,创建子进程,通过管道发送数据,接受管道数据,关闭句柄
CreatePipe(hReadPipe,hWritePipe,LPSECURITY_ATTRIBUTES lpPipeAttributes,PipeSize);
CreateProcess(_T("SubProcess.exe"), (LPTSTR)BufferData, NULL, NULL, TRUE/允许继承句柄/, CREATE_NEW_CONSOLE/让子进程拥有一个黑窗口/, NULL, NULL,&StartupInfo, &ProcessInfo);
WriteFile(m_hPipeWrite, (LPCTSTR)data, data.GetLength()2, &data_write, NULL)
ReadFile((HANDLE)(PipeRead), data, 4096, &data_read, NULL)
Close(Pipe)
子进程:从父进程获得管道句柄,发送和接受数据,关闭句柄
m_hPipeRead = GetStdHandle(STD_INPUT_HANDLE);
m_hPipeWrite = GetStdHandle(STD_OUTPUT_HANDLE);
Read Or Write
Close(Pipe)
b.命名管道(网络,允许异步操作,面向消息和全双工,服务器和客户端都可以发送和接收数据)
异步Io结构,(该结构体内有一个event事件,当有客户端进行连接时变为有信号)
多个管道可以由同一个命名,几个客户端使用相同名称的管道和同一个服务器通信
Server端:创建命名管道,创建事件对象,等待客户端连接,检测I/0是否已经完成,从管道中读取或写入信息,断开连接,关闭句柄
PipeHandle = CreateNamedPipe(PipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE | PIPE_WAIT, ConnectCount, 0, 0, 1000, NULL);
EventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
ConnectNamedPipe(UserData.PipeHandle, &Overlapped);
WaitForSingleObject(UserData.EventHandle, INFINITE);
GetOverlappedResult(UserData.PipeHandle, &Overlapped, &ReturnLength, true)
ReadFile(UserData.PipeHandle, BufferData, 0x1000sizeof(TCHAR), &ReturnLength, NULL)
WriteFile(UserData.PipeHandle, BufferData, _tcslen(BufferData)sizeof(TCHAR), &ReturnLength, NULL);
DisconnectNamedPipe(UserData.PipeHandle);
Close(Pipe)
Client端:等待链接,打开管道,读写数据,关闭句柄
WaitNamedPipe()
NamedPipeHandle = CreateFile(_T("\\.\Pipe\Shine"), GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(NamedPipeHandle, BufferData, _tcslen(BufferData)sizeof(TCHAR), &ReturnLength, NULL);
ReadFile(NamedPipeHandle, BufferData, 0x1000sizeof(TCHAR), &ReturnLength, NULL);
Close(Pipe)
4.剪切板(本机,属于系统维护的公共区域)
系统所有进程都可以无限制的访问剪切板,是不可靠的通信方式
A进程:打开剪切板,申请可移动内存,锁定该内存,写入数据,解锁该内存,设置剪切板内容,关闭剪切板,释放移动内存
OpenClipboard(GetDesktopWindow())
ClipboardHandle = GlobalAlloc(GMEM_MOVEABLE, (strlen(v1) + 1));
VirtualAddress = (TCHAR)GlobalLock(ClipboardHandle);
memcpy(VirtualAddress, v1,(strlen(v1) + 1)sizeof(char));
GlobalUnlock(ClipboardHandle);
SetClipboardData(CF_TEXT, ClipboardHandle);
CloseClipboard();
GlobalFree(ClipboardHandle);
B进程:打开剪切板,获得剪切板里的信息,通过句柄锁定内存,读剪切板数据,解锁内存,关闭剪切板
OpenClipboard(GetDesktopWindow())
ClipboardHandle = GetClipboardData(CF_TEXT);
VirtualAddress = (TCHAR*)GlobalLock(ClipboardHandle);
ViewSize = GlobalSize(ClipboardHandle);
memcpy(v5, VirtualAddress, ViewSize);
GlobalUnlock(ClipboardHandle);
CloseClipboard();
5.父子进程间通信
a.句柄继承
父进程:创建一个安全描述结构体并设置允许句柄被继承,创建一个事件对象(作为数据和通知),创建一个子进程并将当前进程中数据以命令行参数(或环境变量)形式传递到子进程
SECURITY_ATTRIBUTES SecurityAttributes;
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.bInheritHandle = TRUE; //允许句柄继承
SecurityAttributes.lpSecurityDescriptor = NULL;
EventHandle = CreateEvent(&SecurityAttributes,TRUE,FALSE,NULL);//EventHandle到BufferData需要字符串格式转换
CreateProcess(_T("SubProcess.exe"), (LPTSTR)BufferData, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL,&StartupInfo, &ProcessInfo);//命令行参数
CreateProcess(_T("SubProcess.exe"), NULL, NULL, NULL, TRUE,CreateFlags | CREATE_NEW_CONSOLE,/表示环境参数的格式。如果设置了此标志,lpEnvironment所指向的环境块将使用Unicode字符。否则,环境块使用ANSI字符/(LPVOID)BufferData,/新的环境变量内存块/NULL, &StartupInfo, &ProcessInfo);//环境变量
WaitForSingleObject(EventHandle, INFINITE);
CloseHandle(EventHandle);
子进程:接受命令行参数(或环境变量),设置授信状态,关闭句柄
_stscanf_s((TCHAR*)argv[0], _T("%d"), &EventHandle);//获得命令行参数
BufferLength = GetEnvironmentVariable(BufferData, NULL, 0);
GetEnvironmentVariable(BufferData, v5, BufferLength + 1);
_stscanf_s(v5, _T("%d"), &EventHandle);//获得环境变量
SetEvent(EventHandle);//授信状态
CloseHandle(EventHandle);
b.父子进程通信:父进程等待子进程完成初始化,父进程可以将一条信息发送或发布到由子进程中的一个进程创建的窗口
父进程:(一些准备工作,如上)创建子进程,给子进程初始化的时间,通过窗口标题名称获得窗口句柄,向目标窗口发送信息,等待触信,关闭句柄
CreateProcess(_T("SubProcess.exe"),NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo);
WaitForInputIdle(ProcessInfo.hProcess, 1000);
SendMessage(v1, UM_SEND, NULL, (LPARAM)EventHandle);
WaitForSingleObject(EventHandle, INFINITE);
CloseHandle(EventHandle);
子进程:创建一个模态对话框,判断消息类型,接受父进程传递的消息,显示在界面上,授信,关闭句柄
DialogBoxParam(Instance, MAKEINTRESOURCE(IDD_DIALOG), NULL, (DLGPROC)CallBackRoutine, NULL);
__EventHandle = (HANDLE)ParameterData2;
TCHAR BufferData[20] = { 0 };
_stprintf(BufferData, _T("0x%p"), ParameterData2);
SetDlgItemText(Hwnd, IDC_STATIC_SHOW, BufferData);
SetEvent(__EventHandle);
CloseHandle(EventHandle);
c.改变句柄标志,控制继承的子进程
(父进程生成了一个内核对象,得到了一个可继承的句柄,生成了两个子进程,但是父进程只希望其中的一个子进程继承内核对象句柄。)
父进程:获得当前进程伪句柄,将伪句柄转为真句柄,关闭句柄继承标志,创建子进程A,打开句柄继承标志,创建子进程B,关闭句柄
PseudoProcessHandle = GetCurrentProcess();
DuplicateHandle(GetCurrentProcess(), PseudoProcessHandle, GetCurrentProcess(), &RealProcessHandle,0, FALSE, DUPLICATE_SAME_ACCESS);
SetHandleInformation(RealProcessHandle, HANDLE_FLAG_INHERIT, NULL)
CreateProcess(_T("SubProcess(A).exe"),BufferData, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo);
//用SetHandelInformation函数修改了句柄继承标志,即使在创建进程的时候将继承设置为True,子进程也无法继承父进程的句柄。
SetHandleInformation(RealProcessHandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)
CloseHandle(RealProcessHandle);
子进程A不能接收到父进程传送的数据,子进程B可以
6.网络TCP
Server端:初始化网络环境,创建Socket,绑定端口号IP地址,监听,接受客户端连接,接受信息,向客户端发送信息,关闭套接字
WSADATA data{0};
WSAStartup(MAKEWORD(2,2), &data);
SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//①
sockaddr_in serverAddr{0};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8888);
inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr);
bind(server, (SOCKADDR)&serverAddr,sizeof(serverAddr));//②
listen(server,SOMAXCONN);//③
SOCKET client = accept(server, (SOCKADDR)&clientAddr, &size);//④
recv(client, buff, 0x100, 0);//⑤
send(client, "有本早奏,无本退朝!", 20, 0);//⑥
closesocket(server);
Client端:初始化网络环境,创建SOCKET,绑定端口号IP地址,连接服务器,发送信息,接受信息
SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
result=connect(client, (SOCKADDR*)&serverAddr,sizeof(serverAddr));
send(client,"臣刘墉有本要奏!\n",16,0);
recv(client, buff,0x100,0);