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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM