WIndows進程通信(IPC)之管道通信


 

Windows下用管道通信(pipe)實現進程間數據共享
管道是一種用於在進程間共享數據的機制,其實質是一段共享內存。Windows系統為這段共享的內存設計采用數據流I/0的方式來訪問。由一個進程讀、另一個進程寫,類似於一個管道兩端,因此這種進程間的通信方式稱作“管道”。
管道分為匿名管道和命名管道。

匿名管道只能在父子進程間進行通信,不能在網絡間通信,而且數據傳輸是單向的,只能一端寫,另一端讀。

命令管道可以在任意進程間通信,通信是雙向的,任意一端都可讀可寫,但是在同一時間只能有一端讀、一端寫。
創建匿名管道:

1. 定義安全屬性結構體:

SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;//表示可被子進程所繼承 
sa.lpSecurityDescriptor = NULL; //安全描述符號一般都設置成NULL,即默認描述符
sa.nLength = sizeof(SECURITY_ATTRIBUTES); //管道長度

 

其中SECURITY_ATTRIBUTES結構體的定義為:

typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

 

2. 創建管道:

BOOL WINAPI CreatePipe(
PHANDLE hReadPipe,//讀取端句柄
PHANDLE hWritePipe,//輸入端句柄
LPSECURITY_ATTRIBUTES lpPipeAttributes,//安全屬性
DWORD nSize// 管道的緩沖區容量,NULL表示默認大小
);

 



3. 讀取管道內數據:

BOOL ReadFile(
HANDLE hFile,//句柄,可以是標准輸入輸出流或文件或管道
LPVOID lpBuffer, //讀取的數據寫入緩沖區
DWORD nNumberOfBytesToRead,//指定讀取的字節數
LPDWORD lpNumberOfBytesRead,//實際讀取的字節數
LPOVERLAPPED lpOverlapped//用於異步操作,一般置為NULL
);

 

4. 向管道內寫入數據:

BOOL WriteFile(
HANDLE hFile,//句柄,同上
LPCVOID lpBuffer,//指定待寫入的數據
DWORD nNumberOfBytesToWrite,//寫入的數據量
LPDWORDlp NumberOfBytesWritten,//實際要寫的數據量
LPOVERLAPPED lpOverlapped//一般置為NULL
);

 



5. 為實現父子進程間的通信,需要對子進程的管道進行重定向:
我們知道創建子進程函數 CreateProcess中有一個參數STARUIINFO,默認情況下子進程的輸入輸出管道是標准輸入輸出流,可以通過下面的方法實現管道重定向:

STARTUPINFO si; 
si.hStdInput = hPipeInputRead; //輸入由標准輸入 -> 從管道中讀取 
si.hStdOutput = hPipeOutputWrite; //輸出由標准輸出 -> 輸出到管道 


創建命名管道:
命名管道有點類似我們常聽見的服務器端和客戶端,管道正好起着傳輸正如他的名字,命名管道有自己的名字,首先要指定管道名,管道名遵循的格式為:

\\.\pipe\pipename。最多可達256個字符的長度,而且不區分大小寫
例如:"\\\\.\\pipe\\Name_pipe_demon_get"

服務器端創建命名管道

HANDLE WINAPI CreateNamedPipe(

LPCTSTRlpName,//管道名
DWORD dwOpenMode,//管道打開方式
//PIPE_ACCESS_DUPLEX 該管道是雙向的,服務器和客戶端進程都可以從管道讀取或者向管道寫入數據。
//PIPE_ACCESS_INBOUND 該管道中數據是從客戶端流向服務端,即客戶端只能寫,服務端只能讀。
//PIPE_ACCESS_OUTBOUND 該管道中數據是從服務端流向客戶端,即客戶端只能讀,服務端只能寫。
DWORD dwPipeMode,//管道的模式
//PIPE_TYPE_BYTE 數據作為一個連續的字節數據流寫入管道。
//PIPE_TYPE_MESSAGE 數據用數據塊(名為“消息”或“報文”)的形式寫入管道。
//PIPE_READMODE_BYTE 數據以單獨字節的形式從管道中讀出。
//PIPE_READMODE_MESSAGE 數據以名為“消息”的數據塊形式從管道中讀出(要求指定PIPE_TYPE_MESSAGE)。
//PIPE_WAIT 同步操作在等待的時候掛起線程。
//PIPE_NOWAIT 同步操作立即返回。
DWORD nMaxInstances,//表示該管道所能夠創建的最大實例數量。必須是1到常數PIPE_UNLIMITED_INSTANCES(255)間的一個值。
DWORD nOutBufferSize,//表示管道的輸出緩沖區容量,為0表示使用默認大小。
DWORD nInBufferSize,//表示管道的輸入緩沖區容量,為0表示使用默認大小。
DWORD nDefaultTimeOut,//表示管道的默認等待超時。
LPSECURITY_ATTRIBUTES lpSecurityAttributes//表示管道的安全屬性。
);

 

創建完成后等待連接

BOOL WINAPI ConnectNamedPipe(
HANDLE hNamedPipe,//命名管道句柄
LPOVERLAPPED lpOverlapped//一般為NULL
);

服務器端就緒后,客戶端開始連接

BOOL WINAPI WaitNamedPipe(
LPCTSTR lpNamedPipeName,//命名管道名稱
DWORD nTimeOut//等待時長
);

 


連接成功后,打開管道進行數據通信,使用CreateFile,ReadFile和WriteFile,前面匿名管道已經給出了具體使用方法。
下面來看一個具體的例子
A程序作為服務器,不斷從B程序接收數據,並發送到C程序中:

#include <stdio.h>
#include <conio.h> 
#include <tchar.h>
#include <Windows.h>
#include <process.h>
#include <stdlib.h>
const char *pStrPipeNameGet = "\\\\.\\pipe\\Name_pipe_demon_get";
const char *pStrPipeNameSend = "\\\\.\\pipe\\Name_pipe_demon_send";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];
DWORD dwLen;
HANDLE get, mSend, mutex;
LPCRITICAL_SECTION cs;

WCHAR* toWChar(const char *c){
WCHAR wszClassName[256];
memset(wszClassName, 0, sizeof(wszClassName));
MultiByteToWideChar(CP_ACP, 0, c, strlen(c) + 1, wszClassName,
sizeof(wszClassName) / sizeof(wszClassName[0]));
return wszClassName;
}
void beginGetThread(PVOID p){
printf("服務器Get\n");
printf("等待連接......\n");

HANDLE hPipe = CreateNamedPipe(toWChar(pStrPipeNameGet), PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);
if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待連接。 
{
printf("連接成功,開始接收數據\n");
while (true)
{
WaitForSingleObject(mutex, INFINITE);
EnterCriticalSection(cs);
//接收客戶端發送的數據 
ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);
printf("接收到來自A的數據長度為%d字節\n", dwLen);
printf("具體數據內容如下:");
int bufSize;
for (bufSize = 0; bufSize < (int)dwLen; bufSize++){
putchar(buf[bufSize]);
}
LeaveCriticalSection(cs);
Sleep(500);
ReleaseSemaphore(mutex, 1, NULL);
putchar('\n');
}
}
else
{
printf("連接失敗\n");
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);//關閉管道
}

void beginSendThread(PVOID p){
printf("服務器Send\n");
printf("等待連接......\n");

HANDLE hPipe = CreateNamedPipe(toWChar(pStrPipeNameSend), PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);
if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待連接。 
{
printf("連接成功,開始發送緩沖區數據至B\n");
while (true)
{
WaitForSingleObject(mutex, INFINITE);
EnterCriticalSection(cs);
WriteFile(hPipe, buf, (int)dwLen, &dwLen, NULL);
LeaveCriticalSection(cs);
Sleep(500);
ReleaseSemaphore(mutex, 1, NULL);
}
}
else
{
printf("連接失敗\n");
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);//關閉管道
}
int _tmain(int argc, _TCHAR* argv[])
{
cs = (LPCRITICAL_SECTION)malloc(sizeof(LPCRITICAL_SECTION));
InitializeCriticalSection(cs);
mutex = CreateSemaphore(NULL, 1, 1, TEXT("mutex"));
_beginthread(beginGetThread, NULL, NULL);
_beginthread(beginSendThread, NULL, NULL);
Sleep(INFINITE);
DeleteCriticalSection(cs);
return 0;
}

 

B程序不斷接收從鍵盤輸入的數據,數據以回車結束,並發送給A

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h> 
const char *pStrPipeName = "\\\\.\\pipe\\Name_pipe_demon_get";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];

int _tmain(int argc, _TCHAR* argv[])
{
printf("按任意鍵以開始連接Get\n");
_getch();
printf("A開始等待......\n");
if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))
{
printf("Error! 連接Get失敗\n");
return 0;
}
HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
while (true)
{
printf("請輸入要向服務端發送的數據,回車鍵結束,最大1024個字節\n");
DWORD dwLen = 0;
int bufSize;
for (bufSize = 0; bufSize < BUFFER_MAX_LEN; bufSize++){
buf[bufSize] = getchar();
if (buf[bufSize] == '\n') break;
}
//向服務端發送數據 
if (WriteFile(hPipe, buf, bufSize, &dwLen, NULL)){
printf("數據寫入完畢共%d字節\n", dwLen);
}
else
{
printf("數據寫入失敗\n");
}
Sleep(1000);
}
CloseHandle(hPipe);
return 0;
}

 

C程序接收到從A發送來的數據,並轉換成大寫寫入文件

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <conio.h> 
const char *pStrPipeName = "\\\\.\\pipe\\Name_pipe_demon_send";
const int BUFFER_MAX_LEN = 1024;
char buf[BUFFER_MAX_LEN];
DWORD dwLen = 0;

int _tmain(int argc, _TCHAR* argv[])
{
printf("按任意鍵以開始連接Send\n");
_getch();
printf("B開始等待......\n");
if (!WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER))
{
printf("Error! 連接Send失敗\n");
return 0;
}
HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
while (true)
{
// 接收服務端發回的數據
ReadFile(hPipe, buf, BUFFER_MAX_LEN, &dwLen, NULL);//讀取管道中的內容(管道是一種特殊的文件) 
printf("接收服務端發來的信息,長度為%d字節\n", dwLen);
printf("具體數據內容如下:");
HANDLE hWrite = CreateFile(_T("data.txt"), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
for (int j = 0; j <= (int )dwLen; j++){
putchar(buf[j]);
buf[j] = toupper(buf[j]);
}
putchar('\n');
SetFilePointer(hWrite, NULL, NULL, FILE_END);
WriteFile(hWrite, buf, (int)dwLen, NULL, NULL);
WriteFile(hWrite, "\n", 1, NULL, NULL);
CloseHandle(hWrite);
Sleep(1000);
}
CloseHandle(hPipe);
return 0;
} 

 


免責聲明!

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



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