複製鏈接
請複製以下鏈接發送給好友

管道操作

鎖定
Windows和Linux下多個進程之間的一種通信手段,和其他通信手段相比,管道有它自己的限制和特點,管道實際上是一段共享內存區,進程把共享消息放在那裏。並通過一些 API 提供信息交換。
中文名
管道操作
適用領域
Windows、Linux

管道操作基本信息

管道是兩個頭的東西,每個頭各連接一個進程或者同一個進程的不同代碼,按照管道的類別分有兩種管道,匿名的和命名的;按照管道的傳輸方向分也可以分成兩種,單向的雙向的。根據管道的特點,命名管道通常用在網絡環境下不同計算機上運行的進程之間的通信(當然也可以用在同一台機的不同進程中)它可以是單向或雙向的;而匿名管道只能用在同一台計算機中,它只能是單向的。匿名管道其實是通過用給了一個指定名字的有名管道來實現的。
使用管道的好處在於:讀寫它使用的是對文件操作的 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 查詢子進程有沒有輸出,原形如下:
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 感到陌生的話,請先閲讀上一篇教程。