CreatePipe匿名管道通信
生活随笔
收集整理的這篇文章主要介紹了
CreatePipe匿名管道通信
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
管道(Pipe)實(shí)際是用于進(jìn)程間通信的一段共享內(nèi)存,創(chuàng)建管道的進(jìn)程稱(chēng)為管道服務(wù)器,連接到一個(gè)管道的進(jìn)程為管道客戶(hù)機(jī)。一個(gè)進(jìn)程在向管道寫(xiě)入數(shù)據(jù)后,另一進(jìn)程就可以從管道的另一端將其讀取出來(lái)。匿名管道(Anonymous Pipes)是在父進(jìn)程和子進(jìn)程間單向傳輸數(shù)據(jù)的一種未命名的管道,只能在本地計(jì)算機(jī)中使用,而不可用于網(wǎng)絡(luò)間的通信。
匿名管道實(shí)施細(xì)則
匿名管道由CreatePipe()函數(shù)創(chuàng)建,該函數(shù)在創(chuàng)建匿名管道的同時(shí)返回兩個(gè)句柄:管道讀句柄和管道寫(xiě)句柄。CreatePipe()的函數(shù)原型為:
BOOL CreatePipe(PHANDLE hReadPipe, // 指向讀句柄的指針
PHANDLE hWritePipe, // 指向?qū)懢浔闹羔?br /> LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全屬性的指針
DWORD nSize // 管道大小
);?
通過(guò)hReadPipe和hWritePipe所指向的句柄可分別以只讀、只寫(xiě)的方式去訪問(wèn)管道。在使用匿名管道通信時(shí),服務(wù)器進(jìn)程必須將其中的一個(gè)句柄傳送給客戶(hù)機(jī)進(jìn)程。句柄的傳遞多通過(guò)繼承來(lái)完成,服務(wù)器進(jìn)程也允許這些句柄為子進(jìn)程所繼承。除此之外,進(jìn)程也可以通過(guò)諸如DDE或共享內(nèi)存等形式的進(jìn)程間通信將句柄發(fā)送給與其不相關(guān)聯(lián)的進(jìn)程。?
在調(diào)用CreatePipe()函數(shù)時(shí),如果管道服務(wù)器將lpPipeAttributes 指向的SECURITY_ATTRIBUTES數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)成員bInheritHandle設(shè)置為T(mén)RUE,那么CreatePipe()創(chuàng)建的管道讀、寫(xiě)句柄將會(huì)被繼承。管道服務(wù)器可調(diào)用DuplicateHandle()函數(shù)改變管道句柄的繼承。管道服務(wù)器可以為一個(gè)可繼承的管道句柄創(chuàng)建一個(gè)不可繼承的副本或是為一個(gè)不可繼承的管道句柄創(chuàng)建一個(gè)可繼承的副本。CreateProcess()函數(shù)還可以使管道服務(wù)器有能力決定子進(jìn)程對(duì)其可繼承句柄是全部繼承還是不繼承。
在生成子進(jìn)程之前,父進(jìn)程首先調(diào)用Win32 API SetStdHandle()使子進(jìn)程、父進(jìn)程可共用標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤句柄。當(dāng)父進(jìn)程向子進(jìn)程發(fā)送數(shù)據(jù)時(shí),用SetStdHandle()將管道的讀句柄賦予標(biāo)準(zhǔn)輸入句柄;在從子進(jìn)程接收數(shù)據(jù)時(shí),則用SetStdHandle()將管道的寫(xiě)句柄賦予標(biāo)準(zhǔn)輸出(或標(biāo)準(zhǔn)錯(cuò)誤)句柄。然后,父進(jìn)程可以調(diào)用進(jìn)程創(chuàng)建函數(shù)CreateProcess()生成子進(jìn)程。如果父進(jìn)程要發(fā)送數(shù)據(jù)到子進(jìn)程,父進(jìn)程可調(diào)用WriteFile()將數(shù)據(jù)寫(xiě)入到管道(傳遞管道寫(xiě)句柄給函數(shù)),子進(jìn)程則調(diào)用GetStdHandle()取得管道的讀句柄,將該句柄傳入ReadFile()后從管道讀取數(shù)據(jù)。
如果是父進(jìn)程從子進(jìn)程讀取數(shù)據(jù),那么由子進(jìn)程調(diào)用GetStdHandle()取得管道的寫(xiě)入句柄,并調(diào)用WriteFile()將數(shù)據(jù)寫(xiě)入到管道。然后,父進(jìn)程調(diào)用ReadFile()從管道讀取出數(shù)據(jù)(傳遞管道讀句柄給函數(shù))。
在用WriteFile()函數(shù)向管道寫(xiě)入數(shù)據(jù)時(shí),只有在向管道寫(xiě)完指定字節(jié)的數(shù)據(jù)后或是在有錯(cuò)誤發(fā)生時(shí)函數(shù)才會(huì)返回。如管道緩沖已滿(mǎn)而數(shù)據(jù)還沒(méi)有寫(xiě)完,WriteFile()將要等到另一進(jìn)程對(duì)管道中數(shù)據(jù)讀取以釋放出更多可用空間后才能夠返回。管道服務(wù)器在調(diào)用CreatePipe()創(chuàng)建管道時(shí)以參數(shù)nSize對(duì)管道的緩沖大小作了設(shè)定。
匿名管道并不支持異步讀、寫(xiě)操作,這也就意味著不能在匿名管道中使用ReadFileEx()和WriteFileEx(),而且ReadFile()和WriteFile()中的lpOverLapped參數(shù)也將被忽略。匿名管道將在讀、寫(xiě)句柄都被關(guān)閉后退出,也可以在進(jìn)程中調(diào)用CloseHandle()函數(shù)來(lái)關(guān)閉此句柄
/
匿名管道程序示例
總的來(lái)說(shuō),匿名管道程序是比較簡(jiǎn)單的。在下面將要給出的程序示例中,將由父進(jìn)程(管道服務(wù)器)創(chuàng)建一個(gè)子進(jìn)程(管道客戶(hù)機(jī)),子進(jìn)程回見(jiàn)個(gè)其全部的標(biāo)準(zhǔn)輸出發(fā)送到匿名管道中,父進(jìn)程再?gòu)墓艿雷x取數(shù)據(jù),一直到子進(jìn)程關(guān)閉管道的寫(xiě)句柄。其中,匿名管道服務(wù)器程序的實(shí)現(xiàn)清單如下:
STARTUPINFO si;
PROCESS_INFORMATION pi;
char ReadBuf[100];
DWORD ReadNum;
HANDLE hRead; // 管道讀句柄
HANDLE hWrite; // 管道寫(xiě)句柄
BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 創(chuàng)建匿名管道
if (bRet == TRUE)
printf("成功創(chuàng)建匿名管道!\n");
else
printf("創(chuàng)建匿名管道失敗,錯(cuò)誤代碼:%d\n", GetLastError());
// 得到本進(jìn)程的當(dāng)前標(biāo)準(zhǔn)輸出
HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE);
// 設(shè)置標(biāo)準(zhǔn)輸出到匿名管道
SetStdHandle(STD_OUTPUT_HANDLE, hWrite);
GetStartupInfo(&si); // 獲取本進(jìn)程的STARTUPINFO結(jié)構(gòu)信息
bRet = CreateProcess(NULL, "Client.exe", NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi); // 創(chuàng)建子進(jìn)程
SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢復(fù)本進(jìn)程的標(biāo)準(zhǔn)輸出
if (bRet == TRUE) // 輸入信息
printf("成功創(chuàng)建子進(jìn)程!\n");
else
printf("創(chuàng)建子進(jìn)程失敗,錯(cuò)誤代碼:%d\n", GetLastError());
CloseHandle(hWrite); // 關(guān)閉寫(xiě)句柄
// 讀管道直至管道關(guān)閉
while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))
{
ReadBuf[ReadNum] = '\0';
printf("從管道[%s]讀取%d字節(jié)數(shù)據(jù)\n", ReadBuf, ReadNum);
}
if (GetLastError() == ERROR_BROKEN_PIPE) // 輸出信息
printf("管道被子進(jìn)程關(guān)閉\n");
else
printf("讀數(shù)據(jù)錯(cuò)誤,錯(cuò)誤代碼:%d\n", GetLastError());?
在本示例中,將當(dāng)前進(jìn)程的標(biāo)準(zhǔn)輸出設(shè)置為使用匿名管道,再創(chuàng)建子進(jìn)程,子進(jìn)程將繼承父進(jìn)程的標(biāo)準(zhǔn)輸出,然后再將父進(jìn)程的標(biāo)準(zhǔn)輸出恢復(fù)為其初始狀態(tài)。于是父進(jìn)程便可從管道讀取數(shù)據(jù),直到有錯(cuò)誤發(fā)生或關(guān)閉管道寫(xiě)入端的所有句柄。創(chuàng)建的子進(jìn)程只是向標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤發(fā)送一些文本信息,其中發(fā)送給標(biāo)準(zhǔn)輸出的文本將重定向輸出到管道,發(fā)送給標(biāo)準(zhǔn)錯(cuò)誤的文本將不改變輸出。下面給出子進(jìn)程的實(shí)現(xiàn)代碼:
int main(int argc, char* argv[])
{
for (int i = 0; i < 100; i++) // 發(fā)送一些數(shù)據(jù)到標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤
{
printf("i = %d\n", i); // 打印提示
cout << "標(biāo)準(zhǔn)輸出:" << i << endl; // 打印到標(biāo)準(zhǔn)輸出
cerr << "標(biāo)準(zhǔn)錯(cuò)誤:" << i << endl; // 打印到標(biāo)準(zhǔn)錯(cuò)誤
}
return 0;
匿名管道實(shí)施細(xì)則
匿名管道由CreatePipe()函數(shù)創(chuàng)建,該函數(shù)在創(chuàng)建匿名管道的同時(shí)返回兩個(gè)句柄:管道讀句柄和管道寫(xiě)句柄。CreatePipe()的函數(shù)原型為:
BOOL CreatePipe(PHANDLE hReadPipe, // 指向讀句柄的指針
PHANDLE hWritePipe, // 指向?qū)懢浔闹羔?br /> LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全屬性的指針
DWORD nSize // 管道大小
);?
通過(guò)hReadPipe和hWritePipe所指向的句柄可分別以只讀、只寫(xiě)的方式去訪問(wèn)管道。在使用匿名管道通信時(shí),服務(wù)器進(jìn)程必須將其中的一個(gè)句柄傳送給客戶(hù)機(jī)進(jìn)程。句柄的傳遞多通過(guò)繼承來(lái)完成,服務(wù)器進(jìn)程也允許這些句柄為子進(jìn)程所繼承。除此之外,進(jìn)程也可以通過(guò)諸如DDE或共享內(nèi)存等形式的進(jìn)程間通信將句柄發(fā)送給與其不相關(guān)聯(lián)的進(jìn)程。?
在調(diào)用CreatePipe()函數(shù)時(shí),如果管道服務(wù)器將lpPipeAttributes 指向的SECURITY_ATTRIBUTES數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)成員bInheritHandle設(shè)置為T(mén)RUE,那么CreatePipe()創(chuàng)建的管道讀、寫(xiě)句柄將會(huì)被繼承。管道服務(wù)器可調(diào)用DuplicateHandle()函數(shù)改變管道句柄的繼承。管道服務(wù)器可以為一個(gè)可繼承的管道句柄創(chuàng)建一個(gè)不可繼承的副本或是為一個(gè)不可繼承的管道句柄創(chuàng)建一個(gè)可繼承的副本。CreateProcess()函數(shù)還可以使管道服務(wù)器有能力決定子進(jìn)程對(duì)其可繼承句柄是全部繼承還是不繼承。
在生成子進(jìn)程之前,父進(jìn)程首先調(diào)用Win32 API SetStdHandle()使子進(jìn)程、父進(jìn)程可共用標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤句柄。當(dāng)父進(jìn)程向子進(jìn)程發(fā)送數(shù)據(jù)時(shí),用SetStdHandle()將管道的讀句柄賦予標(biāo)準(zhǔn)輸入句柄;在從子進(jìn)程接收數(shù)據(jù)時(shí),則用SetStdHandle()將管道的寫(xiě)句柄賦予標(biāo)準(zhǔn)輸出(或標(biāo)準(zhǔn)錯(cuò)誤)句柄。然后,父進(jìn)程可以調(diào)用進(jìn)程創(chuàng)建函數(shù)CreateProcess()生成子進(jìn)程。如果父進(jìn)程要發(fā)送數(shù)據(jù)到子進(jìn)程,父進(jìn)程可調(diào)用WriteFile()將數(shù)據(jù)寫(xiě)入到管道(傳遞管道寫(xiě)句柄給函數(shù)),子進(jìn)程則調(diào)用GetStdHandle()取得管道的讀句柄,將該句柄傳入ReadFile()后從管道讀取數(shù)據(jù)。
如果是父進(jìn)程從子進(jìn)程讀取數(shù)據(jù),那么由子進(jìn)程調(diào)用GetStdHandle()取得管道的寫(xiě)入句柄,并調(diào)用WriteFile()將數(shù)據(jù)寫(xiě)入到管道。然后,父進(jìn)程調(diào)用ReadFile()從管道讀取出數(shù)據(jù)(傳遞管道讀句柄給函數(shù))。
在用WriteFile()函數(shù)向管道寫(xiě)入數(shù)據(jù)時(shí),只有在向管道寫(xiě)完指定字節(jié)的數(shù)據(jù)后或是在有錯(cuò)誤發(fā)生時(shí)函數(shù)才會(huì)返回。如管道緩沖已滿(mǎn)而數(shù)據(jù)還沒(méi)有寫(xiě)完,WriteFile()將要等到另一進(jìn)程對(duì)管道中數(shù)據(jù)讀取以釋放出更多可用空間后才能夠返回。管道服務(wù)器在調(diào)用CreatePipe()創(chuàng)建管道時(shí)以參數(shù)nSize對(duì)管道的緩沖大小作了設(shè)定。
匿名管道并不支持異步讀、寫(xiě)操作,這也就意味著不能在匿名管道中使用ReadFileEx()和WriteFileEx(),而且ReadFile()和WriteFile()中的lpOverLapped參數(shù)也將被忽略。匿名管道將在讀、寫(xiě)句柄都被關(guān)閉后退出,也可以在進(jìn)程中調(diào)用CloseHandle()函數(shù)來(lái)關(guān)閉此句柄
/
匿名管道程序示例
總的來(lái)說(shuō),匿名管道程序是比較簡(jiǎn)單的。在下面將要給出的程序示例中,將由父進(jìn)程(管道服務(wù)器)創(chuàng)建一個(gè)子進(jìn)程(管道客戶(hù)機(jī)),子進(jìn)程回見(jiàn)個(gè)其全部的標(biāo)準(zhǔn)輸出發(fā)送到匿名管道中,父進(jìn)程再?gòu)墓艿雷x取數(shù)據(jù),一直到子進(jìn)程關(guān)閉管道的寫(xiě)句柄。其中,匿名管道服務(wù)器程序的實(shí)現(xiàn)清單如下:
STARTUPINFO si;
PROCESS_INFORMATION pi;
char ReadBuf[100];
DWORD ReadNum;
HANDLE hRead; // 管道讀句柄
HANDLE hWrite; // 管道寫(xiě)句柄
BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 創(chuàng)建匿名管道
if (bRet == TRUE)
printf("成功創(chuàng)建匿名管道!\n");
else
printf("創(chuàng)建匿名管道失敗,錯(cuò)誤代碼:%d\n", GetLastError());
// 得到本進(jìn)程的當(dāng)前標(biāo)準(zhǔn)輸出
HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE);
// 設(shè)置標(biāo)準(zhǔn)輸出到匿名管道
SetStdHandle(STD_OUTPUT_HANDLE, hWrite);
GetStartupInfo(&si); // 獲取本進(jìn)程的STARTUPINFO結(jié)構(gòu)信息
bRet = CreateProcess(NULL, "Client.exe", NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi); // 創(chuàng)建子進(jìn)程
SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢復(fù)本進(jìn)程的標(biāo)準(zhǔn)輸出
if (bRet == TRUE) // 輸入信息
printf("成功創(chuàng)建子進(jìn)程!\n");
else
printf("創(chuàng)建子進(jìn)程失敗,錯(cuò)誤代碼:%d\n", GetLastError());
CloseHandle(hWrite); // 關(guān)閉寫(xiě)句柄
// 讀管道直至管道關(guān)閉
while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))
{
ReadBuf[ReadNum] = '\0';
printf("從管道[%s]讀取%d字節(jié)數(shù)據(jù)\n", ReadBuf, ReadNum);
}
if (GetLastError() == ERROR_BROKEN_PIPE) // 輸出信息
printf("管道被子進(jìn)程關(guān)閉\n");
else
printf("讀數(shù)據(jù)錯(cuò)誤,錯(cuò)誤代碼:%d\n", GetLastError());?
在本示例中,將當(dāng)前進(jìn)程的標(biāo)準(zhǔn)輸出設(shè)置為使用匿名管道,再創(chuàng)建子進(jìn)程,子進(jìn)程將繼承父進(jìn)程的標(biāo)準(zhǔn)輸出,然后再將父進(jìn)程的標(biāo)準(zhǔn)輸出恢復(fù)為其初始狀態(tài)。于是父進(jìn)程便可從管道讀取數(shù)據(jù),直到有錯(cuò)誤發(fā)生或關(guān)閉管道寫(xiě)入端的所有句柄。創(chuàng)建的子進(jìn)程只是向標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤發(fā)送一些文本信息,其中發(fā)送給標(biāo)準(zhǔn)輸出的文本將重定向輸出到管道,發(fā)送給標(biāo)準(zhǔn)錯(cuò)誤的文本將不改變輸出。下面給出子進(jìn)程的實(shí)現(xiàn)代碼:
int main(int argc, char* argv[])
{
for (int i = 0; i < 100; i++) // 發(fā)送一些數(shù)據(jù)到標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤
{
printf("i = %d\n", i); // 打印提示
cout << "標(biāo)準(zhǔn)輸出:" << i << endl; // 打印到標(biāo)準(zhǔn)輸出
cerr << "標(biāo)準(zhǔn)錯(cuò)誤:" << i << endl; // 打印到標(biāo)準(zhǔn)錯(cuò)誤
}
return 0;
}?
總結(jié)
以上是生活随笔為你收集整理的CreatePipe匿名管道通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。