管道操作

Windows和Linux下多個進程之間的一種通信手段,和其他通信手段相比,管道有它自己的限制和特點,管道實際上是一段共享記憶體區,進程把共享訊息放在那裡。並通過一些 API 提供信息交換。

概述

管道是兩個頭的東西,每個頭各連線一個進程或者同一個進程的不同代碼,按照管道的類別分有兩種管道,匿名的和命名的;按照管道的傳輸方向分也可以分成兩種,單向的雙向的。根據管道的特點,命名管道通常用在網路環境下不同計算機上運行的進程之間的通信(當然也可以用在同一台機的不同進程中)它可以是單向或雙向的;而匿名管道只能用在同一台計算機中,它只能是單向的。匿名管道其實是通過用給了一個指定名字的有名管道來實現的。

使用管道的好處在於:讀寫它使用的是對檔案操作的 api,結果操作管道就和操作檔案一樣。即使你在不同的計算機之間用命名管道來通信,你也不必了解和自己去實現網路間通信的具體細節。

使用說明

命名管道是由伺服器端的進程建立的,管道的命名必須遵循特定的命名方法,就是 "\\.\pipe\管道名",當作為客戶端的進程要使用時,使用"\\計算機名\\pipe\管道名" 來打開使用,具體步驟如下:

服務端通過函式 CreateNamedPipe 創建一個命名管道的實例並返回用於今後操作的句柄,或為已存在的管道創建新的實例。 服務端偵聽來自客戶端的連線請求,該功能通過 ConnectNamedPipe 函式實現。 客戶端通過函式 WaitNamedPipe 來等待管道的出現,如果在逾時值變為零以前,有一個管道可以使用,則 WaitNamedPipe 將返回 True,並通過調用 CreateFile 或 CallNamedPipe 來呼叫對服務端的連線。 此時服務端將接受客戶端的連線請求,成功建立連線,服務端 ConnectNamedPipe 返回 True 建立連線之後,客戶端與伺服器端即可通過 ReadFile 和 WriteFile,利用得到的管道檔案句柄,彼此間進行信息交換。 當客戶端與服務端的通信結束,客戶端調用 CloseFile,服務端接著調用 DisconnectNamedPipe。最後調用函式CloseHandle來關閉該管道。 由於命名管道使用時作為客戶端的程式必須知道管道的名稱,所以更多的用在同一“作者”編寫的伺服器/工作站程式中,你不可能隨便找出一個程式來要求它和你寫的程式來通過命名管道通信。而匿名管道的使用則完全不同,它允許你和完全不相干的進程通信,條件是這個進程通過控制台“console”來輸入輸出,典型的例子是老的 Dos 應用程式,它們在運行時 Windows 為它們開了個 Dos 視窗,它們的輸入輸出就是 console 方式的。還有一些標準的 Win32 程式也使用控制台輸入輸出,如果在 Win32 編程中不想使用圖形界面,你照樣可以使用 AllocConsole 得到一個控制台,然後通過 GetStdHandle 得到輸入或輸出句柄,再通過 WriteConsole 或 WriteFile 把結果輸出到控制台(通常是一個象 Dos 視窗)的螢幕上。雖然這些程式看起來象 Dos 程式,但它們是不折不扣的 Win32 程式,如果你在純 Dos 下使用,就會顯示“The program must run under Windows!”。

一個控制台有三個句柄:標準輸入、標準輸出和和標準錯誤句柄,標準輸入、標準輸出句柄是可以重新定向的,你可以用匿名管道來代替它,這樣一來,你可以在管道的另一端用別的進程來接收或輸入,而控制台一方並沒有感到什麼不同,就象 Dos 下的 > 或者 < 可以重新定向輸出或輸入一樣。通常控制台程式的輸入輸出如下:

(控制台進程output) write ----> 標準輸出設備(一般是螢幕)

(控制台進程input) read <---- 標準輸入設備(一般是鍵盤)

而用管道代替後:

(作為子進程的控制台進程output) write ----> 管道1 ----> read (父進程)

(作為子進程的控制台進程input) read <----> 管道2 <---- write (父進程)

使用匿名管道的步驟如下:

使用 CreatePipe 建立兩個管道,得到管道句柄,一個用來輸入,一個用來輸出 準備執行控制台子進程,首先使用 GetStartupInfo 得到 StartupInfo 使用第一個管道句柄代替 StartupInfo 中的 hStdInput,第二個代替 hStdOutput、hStdError,即標準輸入、輸出、錯誤句柄 使用 CreateProcess 執行子進程,這樣建立的子進程輸入和輸出就被定向到管道中 父進程通過 ReadFile 讀第二個管道來獲得子進程的輸出,通過 WriteFile 寫第一個管道來將輸入寫到子進程 父進程可以通過 PeekNamedPipe 來查詢子進程有沒有輸出 子進程結束後,要通過 CloseHandle 來關閉兩個管道。 下面是具體的說明和定義:

1. 建立匿名管道使用 CreatePipe 原形如下:

BOOL CreatePipe(

PHANDLE hReadPipe, // address of variable for read handle

PHANDLE hWritePipe, // address of variable for write handle

LPSECURITY_ATTRIBUTES lpPipeAttributes, // pointer to security attributes

DWORD nSize // number of bytes reserved for pipe

);

當管道建立後,結構中指向的 hReadPipe 和 hWritePipe 可用來讀寫管道,當然由於匿名管道是單向的,你只能使用其中的一個句柄,參數中的 SECURITY_ATTRIBUTES 的結構必須填寫,定義如下:

typedef struct_SECURITY_ATTRIBUTES{

DWORD nLength: //定義以位元組為單位的此結構的長度

LPVOID lpSecurityDescriptor; //指向控制這個對象共享的安全描述符,如果為NULL這個對象將被分配一個預設的安全描述

BOOL bInheritHandle; //當一個新過程被創建時,定義其返回是否是繼承的.供系統API函式使用.

}SECURITY_ATTRIBUTES;

2. 填寫創建子進程用的 STARTUPINFO 結構,一般我們可以先用 GetStartupInfo 來填寫一個預設的結構,然後改動我們用得到的地方,它們是:

hStdInput -- 用其中一個管道的 hWritePipe 代替 hStdOutput、hStdError -- 用另一個管道的 hReadPipe 代替 dwFlags -- 設定為 STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW 表示輸入輸出句柄及 wShowWindow 欄位有效 wShowWindow -- 設定為 SW_HIDE,這樣子進程執行時不顯示視窗。 填寫好以後,就可以用 CreateProcess 來執行子進程了,具體有關執行子進程的操作可以參考上一篇教程《進程控制》

3. 在程式中可以用 PeekNamedPipe 查詢子進程有沒有輸出,原形如下:

BOOL PeekNamedPipe(

HANDLE hNamedPipe, // handle to pipe to copy from

LPVOID lpBuffer, // pointer to data buffer

DWORD nBufferSize, // size, in bytes, of data buffer

LPDWORD lpBytesRead, // pointer to number of bytes read

LPDWORD lpTotalBytesAvail, // pointer to total number of bytes available

LPDWORD lpBytesLeftThisMessage // pointer to unread bytes in this message

);

我們可以將嘗試讀取 nBuffersize 大小的數據,然後可以通過返回的 BytesRead 得到管道中有多少數據,如果不等於零,則表示有數據可以讀取。

4. 用 ReadFile 和 WriteFile 來讀寫管道,它們的參數是完全一樣的,原形如下:

ReadFile or WriteFile(

HANDLE hFile, // handle of file to read 在這裡使用管道句柄

LPVOID lpBuffer, // address of buffer that receives data 緩衝區地址

DWORD nNumberOfBytesToRead, // number of bytes to read 準備讀寫的位元組數

LPDWORD lpNumberOfBytesRead, // address of number of bytes read,實際讀到的或寫入的位元組數

LPOVERLAPPED lpOverlapped // address of structure for data 在這裡用 NULL

);

5. 用 CloseHandle 關閉管道一和管道二的 hReadPipe和 hWritePipe 這四個句柄。

下面給出了一個例子程式,這個程式是上篇教程《進程控制》的例子的擴充,如果你對有的 api 感到陌生的話,請先閱讀上一篇教程。

相關詞條

相關搜尋

熱門詞條

聯絡我們