? 一、管道實(shí)現(xiàn)進(jìn)程間通訊 主要的理論知識(shí) 1.什么是管道以及分類 管道是兩個(gè)頭的東西,每一個(gè)頭各連接一個(gè)進(jìn)程或者同一個(gè)進(jìn)程的不同代碼 ,依照管道的類別分有兩種管道,匿名的和命名的;依照管道的傳輸方向分也能夠分成兩種,單向的雙向的 。依據(jù)管道的特點(diǎn),命名管道通經(jīng)常使用在網(wǎng)絡(luò)環(huán)境下不同計(jì)算機(jī)上執(zhí)行的進(jìn)程之間的通信(當(dāng)然也能夠用在同一臺(tái)機(jī)的不同進(jìn)程中)它能夠是單向或雙向的;而匿名管道僅僅能用在同一臺(tái)計(jì)算機(jī)中,它僅僅能是單向的。匿名管道事實(shí)上是通過(guò)用給了一個(gè)指定名字的有名管道來(lái)實(shí)現(xiàn)的 。 使用管道的優(yōu)點(diǎn)在于:讀寫(xiě)它使用的是對(duì)文件操作的 api ,結(jié)果操作管道就和操作文件一樣。即使你在不同的計(jì)算機(jī)之間用命名管道來(lái)通信,你也不必了解和自己去實(shí)現(xiàn)網(wǎng)絡(luò)間通信的詳細(xì)細(xì)節(jié)。 2.管道的使用 A.命名管道 命名管道是由server端的進(jìn)程建立的,管道的命名必須遵循特定的命名方法,就是 "//./pipe/ 管道名 " ,當(dāng)作為client的進(jìn)程要使用時(shí),使用 "// 計(jì)算機(jī)名 //pipe/ 管道名 " 來(lái)打開(kāi)使用,詳細(xì)過(guò)程例如以下:
服務(wù)端通過(guò)函數(shù) CreateNamedPipe 創(chuàng)建一個(gè)命名管道的實(shí)例并返回用于今后操作的句柄,或?yàn)橐汛嬖诘墓艿绖?chuàng)建新的實(shí)例。服務(wù)端偵聽(tīng)來(lái)自client的連接請(qǐng)求,該功能通過(guò) ConnectNamedPipe 函數(shù)實(shí)現(xiàn)。
client通過(guò)函數(shù) WaitNamedPipe 來(lái)等待管道的出現(xiàn),假設(shè)在超時(shí)值變?yōu)榱阍?jīng),有一個(gè)管道能夠使用 ,則 WaitNamedPipe 將返回 True ,并通過(guò)調(diào)用 CreateFile 或 CallNamedPipe 來(lái)呼叫對(duì)服務(wù)端的連接。
此時(shí)服務(wù)端將接受client的連接請(qǐng)求,成功建立連接,服務(wù)端 ConnectNamedPipe 返回 True 建立連接之后,client與server端就可以通過(guò) ReadFile 和 WriteFile ,利用得到的管道文件句柄,彼此間進(jìn)行信息交換。 當(dāng)client與服務(wù)端的通信結(jié)束,client調(diào)用 CloseFile ,服務(wù)端接著調(diào)用 DisconnectNamedPipe 。最后調(diào)用函數(shù) CloseHandle 來(lái)關(guān)閉該管道。
B.匿名管道 因?yàn)槊艿朗褂脮r(shí)作為client的程序必須知道管道的名稱,所以很多其它的用在同一 “ 作者 ” 編寫(xiě)的server / 工作站程序中,你不可能隨便找出一個(gè)程序來(lái)要求它和你寫(xiě)的程序來(lái)通過(guò)命名管道通信。而匿名管道的使用則全然不同,它同意你和全然不相干的進(jìn)程通信,條件是這個(gè)進(jìn)程通過(guò)控制臺(tái) “console” 來(lái)輸入輸出,典型的樣例是老的 Dos 應(yīng)用程序 ,它們?cè)趫?zhí)行時(shí) Windows 為它們開(kāi)了個(gè) Dos 窗體,它們的輸入輸出就是 console 方式的。另一些標(biāo)準(zhǔn)的 Win32 程序也使用控制臺(tái)輸入輸出,假設(shè)在 Win32 編程中不想使用圖形界面,你照樣能夠使用 AllocConsole 得到一個(gè)控制臺(tái),然后通過(guò) GetStdHandle 得到輸入或輸出句柄,再通過(guò) WriteConsole 或 WriteFile 把結(jié)果輸出到控制臺(tái)(一般是一個(gè)象 Dos 窗體)的屏幕上。盡管這些程序看起來(lái)象 Dos 程序,但它們是不折不扣的 Win32 程序,假設(shè)你在純 Dos 下使用,就會(huì)顯示 “The program must run under Windows!” 。
一個(gè)控制臺(tái)有三個(gè)句柄:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和和標(biāo)準(zhǔn)錯(cuò)誤句柄,標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出句柄是能夠又一次定向 的,你能夠用匿名管道來(lái)取代它,這樣一來(lái),你能夠在管道的還有一端用別的進(jìn)程來(lái)接收或輸入,而控制臺(tái)一方并沒(méi)有感到什么不同,就象 Dos 下的 > 或者 < 能夠又一次定向輸出或輸入一樣。通常控制臺(tái)程序的輸入輸出例如以下:
( 控制臺(tái)進(jìn)程 output) write ----> 標(biāo)準(zhǔn)輸出設(shè)備(通常是屏幕 )
( 控制臺(tái)進(jìn)程 input) read <---- 標(biāo)準(zhǔn)輸入設(shè)備(通常是鍵盤(pán))
而用管道取代后: ( 作為子進(jìn)程的控制臺(tái)進(jìn)程 output) write ----> 管道 1 ----> read ( 父進(jìn)程 ) ( 作為子進(jìn)程的控制臺(tái)進(jìn)程 input) read <----> 管道 2 <---- write ( 父進(jìn)程 ) 使用匿名管道的過(guò)程例如以下: 使用 CreatePipe 建立兩個(gè)管道,得到管道句柄,一個(gè)用來(lái)輸入,一個(gè)用來(lái)輸出 準(zhǔn)備運(yùn)行控制臺(tái)子進(jìn)程,首先使用 GetStartupInfo 得到 StartupInfo 使用第一個(gè)管道句柄取代 StartupInfo 中的 hStdInput ,第二個(gè)取代 hStdOutput 、 hStdError ,即標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤句柄 使用 CreateProcess 運(yùn)行子進(jìn)程,這樣建立的子進(jìn)程輸入和輸出就被定向到管道中 父進(jìn)程通過(guò) ReadFile 讀第二個(gè)管道來(lái)獲得子進(jìn)程的輸出,通過(guò) WriteFile 寫(xiě)第一個(gè)管道來(lái)將輸入寫(xiě)到子進(jìn)程 父進(jìn)程能夠通過(guò) PeekNamedPipe 來(lái)查詢子進(jìn)程有沒(méi)有輸出 子進(jìn)程結(jié)束后,要通過(guò) CloseHandle 來(lái)關(guān)閉兩個(gè)管道。 ? 管道使用的API函數(shù)集
CallNamedPipe 函數(shù) 函數(shù)原型: BOOL CallNamedPipe( LPCTSTR lpNamedPipeName, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, DWORD nTimeOut ); 說(shuō)明 : 這個(gè)函數(shù)由一個(gè)希望通過(guò)管道通信的一個(gè)客戶進(jìn)程調(diào)用。如有可能,它就同一個(gè)管道連接(在必要的情況下等候管道可用)。隨后,它對(duì)指定的數(shù)據(jù)進(jìn)行讀寫(xiě),然后將管道關(guān)閉 返回值 : Long ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù)表 : lpNamedPipeName: LPCTSTR ,指定管道名,採(cǎi)用的形式是: //./ 管道 / 管道名。最多可達(dá) 256 個(gè)字符的長(zhǎng)度,并且不用區(qū)分大寫(xiě)和小寫(xiě)。假設(shè)存在指定名字的一個(gè)管道,則創(chuàng)建那個(gè)管道的一個(gè)新實(shí)例 lpInBuffer: LPVOID ,包括了要寫(xiě)入管道的數(shù)據(jù)的一個(gè)內(nèi)存緩沖區(qū) nInBufferSize: DWORD , lpInBuffer 緩沖區(qū)中的字符數(shù)量 lpOutBuffer : LPVOID , 指定一個(gè)內(nèi)存緩沖區(qū),用于裝載從管道中讀出的數(shù)據(jù) nOutBufferSize : DWORD , 指定一個(gè)長(zhǎng)整數(shù)變量,用于裝載來(lái)自管道的數(shù)據(jù) lpBytesRead : LPDWORD , 指定從管道中讀出的字節(jié)數(shù)。會(huì)閱讀單條消息。如 lpOutBuffer 的容量不夠大,不能容下整條消息,則函數(shù)會(huì)返回 FALSE ,并且 GetLastError 會(huì)設(shè)為 ERROR_MORE_DATA (消息中留下的不論什么字節(jié)都會(huì)丟失) nTimeOut : DWORD ,下列常量之中的一個(gè): 1.??????? NMPWAIT_NOWAIT : ? 如管道不可用,則馬上返回一個(gè)錯(cuò)誤 2.??????? NMPWAIT_WAIT_FOREVER : 永遠(yuǎn)等候管道可用 . 3.??????? NMPWAIT_USE _DEFAULT_WAIT : 使用管道的默認(rèn)超時(shí)設(shè)置,這個(gè)設(shè)置是用 CreateNamedPipe 函數(shù)指定的
ConnectNamedPipe 函數(shù) 函數(shù)原型: BOOL ConnectNamedPipe( HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped ); 說(shuō)明: 指示一臺(tái)server等待下去,直至客戶機(jī)同一個(gè)命名管道連接 返回值: BOOL ,如 lpOverlapped 為 NULL ,那么: 1.????? 如管道已連接,就返回 Ture (非零); ????? 如錯(cuò)誤發(fā)生,或者管道已經(jīng)連接,就返回零( GetLastError 此時(shí)會(huì)返回 ERROR_PIPE_CONNECTED ) 2.???? lpOverlapped 有效,就返回零; ???????如管道已經(jīng)連接, GetLastError 會(huì)返回 ERROR_PIPE_CONNECTED ; ????? 如重疊操作成功完畢,就返回 ERROR_IO_PENDING 。 ???????在這兩種情況下,倘若一個(gè)客戶已關(guān)閉了管道,且server尚未用 DisconnectNamedPipe 函數(shù)同客戶斷開(kāi)連接,那么 GetLastError 都會(huì)返回 ERROR_NO_DATA 參數(shù) : hNamedPipe : HANDLE ,管道的句柄 lpOverlapped : LPOVERLAPPED , ??????? 如設(shè)為 NULL (傳遞 ByVal As Long ),表示將線程掛起,直到一個(gè)客戶同管道連接為止。否則就馬上返回; ???? 此時(shí),如管道尚未連接,客戶同管道連接時(shí)就會(huì)觸發(fā) lpOverlapped 結(jié)構(gòu)中的事件對(duì)象。隨后,可用一個(gè)等待函數(shù)來(lái)監(jiān)視連接 適用平臺(tái) : Windows NT 凝視: 可用這個(gè)函數(shù)將一個(gè)管道換成同還有一個(gè)客戶連接,但首先必須用 DisconnectNamedPipe 函數(shù)斷開(kāi)同當(dāng)前進(jìn)程的連接
CreateNamedPipe 函數(shù) 函數(shù)原型: HANDLE CreateNamedPipe( LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes); 說(shuō)明: 創(chuàng)建一個(gè)命名管道。返回的句柄由管道的server端使用 返回值: HANDLE ,如運(yùn)行成功,返回管道的句柄。 INVALID_HANDLE_VALUE 表示失敗 . 會(huì)設(shè)置 GetLastError 參數(shù): lpName : LPCTSTR ,指定管道名,採(cǎi)用的形式是: //./ 管道 / 管道名 。最多可達(dá) 256 個(gè)字符的長(zhǎng)度,并且不用區(qū)分大寫(xiě)和小寫(xiě)。假設(shè)存在指定名字的一個(gè)管道,則創(chuàng)建那個(gè)管道的一個(gè)新實(shí)例 dwOpenMode : DWORD , 下述常數(shù)組的一個(gè)組合 下述常數(shù)之中的一個(gè)(對(duì)于管道的全部實(shí)例都要一樣): 1.????????? PIPE_ACCESS_DUPLEX 管道是雙向的 2.????????? PIPE_ACCESS_INBOUND 數(shù)據(jù)從client流到server端 3.????????? PIPE_ACCESS_OUTBOUND 數(shù)據(jù)從server端流到client 下述常數(shù)的隨意組合 1.????????? FILE_FLAG_WRITE_THROUGH 在網(wǎng)絡(luò)中建立的字節(jié)型管道內(nèi),強(qiáng)迫數(shù)據(jù)在每次讀寫(xiě)操作的時(shí)候通過(guò)網(wǎng)絡(luò)傳輸。否則傳輸就可能延遲 2.????????? FILE_FLAG_OVERLAPPED 同意(但不要求)用這個(gè)管道進(jìn)行異步(重疊式)操作 常數(shù) WRITE_DAC , WRITE_OWNER 和 ACCESS_ SYSTEM_SECURITY 提供了附加的安全選項(xiàng) dwPipeMode : DWORD ,下述常數(shù)組的一個(gè)組合 下述常數(shù)之中的一個(gè)(管道的全部實(shí)例都必須指定同樣的常數(shù)) 1.????????? PIPE_TYPE_BYTE 數(shù)據(jù)作為一個(gè)連續(xù)的字節(jié)數(shù)據(jù)流寫(xiě)入管道 2.????????? PIPE_TYPE_MESSAGE 數(shù)據(jù)用數(shù)據(jù)塊(名為 “ 消息 ” 或 “ 報(bào)文 ” )的形式寫(xiě)入管道 下述常數(shù)之中的一個(gè): 1.????????? PIPE_READMODE_PIPE 數(shù)據(jù)以單獨(dú)字節(jié)的形式從管道中讀出 2.????????? PIPE_READMODE_MESSAGE 數(shù)據(jù)以名為 “ 消息 ” 的數(shù)據(jù)塊形式從管道中讀出(要求指定 PIPE_TYPE_MESSAGE ) 下述常數(shù)之中的一個(gè): 1.????????? PIPE_WAIT 同步操作在等待的時(shí)候掛起線程 2.????????? PIPE_NOWAIT (不推薦!) 同步操作馬上返回。這樣可為異步傳輸提供一種落后的實(shí)現(xiàn)方法,已由 Win32 的重疊式傳輸機(jī)制代替了 nMaxInstances : DWORD ,這個(gè)管道可以創(chuàng)建的最大實(shí)例數(shù) 量。必須是 1 到常數(shù) PIPE_UNLIMITED_INSTANCES 間的一個(gè)值。它對(duì)于管道的全部實(shí)例來(lái)說(shuō)都應(yīng)是同樣的 nOutBufferSize : DWORD ,建議的輸出緩沖區(qū)長(zhǎng)度;零表示用默認(rèn)設(shè)置 nInBufferSize : DWORD ,建議的輸入緩沖區(qū)長(zhǎng)度;零表示用默認(rèn)設(shè)置 nDefaultTimeOut : DWORD ,管道的默認(rèn)等待超時(shí) 。對(duì)一個(gè)管道的全部實(shí)例來(lái)說(shuō)都應(yīng)同樣 lpSecurityAttributes : LPSECURITY_ATTRIBUTES ,指定一個(gè) SECURITY_ATTRIBUTES 結(jié)構(gòu),或者傳遞零值(將參數(shù)聲明為 ByVal As Long ,并傳遞零值),以便使用不同意繼承的一個(gè)默認(rèn)描寫(xiě)敘述符 適用平臺(tái): Windows NT
CreatePipe 函數(shù) 函數(shù)原型: BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize ); 說(shuō)明: 創(chuàng)建一個(gè)匿名管道 返回值: Long ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù) : phReadPipe : PHANDLE ,指定一個(gè)變量,設(shè)為管道讀入(輸出)端的一個(gè)句柄 phWritePipe : PHANDLE ,指定一個(gè)變量,設(shè)為管道寫(xiě)入(輸入)端的一個(gè)句柄 lpPipeAttributes : LPSECURITY _ATTRIBUTES ,指定一個(gè) SECURITY_ATTRIBUTES 結(jié)構(gòu),或者傳遞零值,以便使用不同意繼承的一個(gè)默認(rèn)描寫(xiě)敘述符 nSize : DWORD ,管道緩沖區(qū)的建議大小。零表示用默認(rèn)值 注解: 匿名管道不同意異步操作,所以如在一個(gè)管道中寫(xiě)入數(shù)據(jù),且緩沖區(qū)已滿,那么除非還有一個(gè)進(jìn)程從管道中讀出數(shù)據(jù),從而騰出了緩沖區(qū)的空間,否則寫(xiě)入函數(shù)不會(huì)返回
DisconnectNamedPipe 函數(shù) 函數(shù)原型: BOOL DisconnectNamedPipe( HANDLE hNamedPipe ); 說(shuō)明: 斷開(kāi)一個(gè)客戶與一個(gè)命名管道的連接(server端與client都可調(diào)用) 返回值: BOOL ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù): hNamedPipe Long ,管道的句柄 適用平臺(tái): Windows NT 注解 :如客戶尚未在它自己那端關(guān)閉管道句柄,下次試圖訪問(wèn)管道的時(shí)候就會(huì)錯(cuò)誤發(fā)生
GetNamedPipeHandleState 函數(shù) 函數(shù)原型: BOOL GetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpState, LPDWORD lpCurInstances, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout, LPTSTR lpUserName, DWORD nMaxUserNameSize); 說(shuō)明 : 獲取一個(gè)命名管道當(dāng)前的狀態(tài)信息 返回值 : BOOL ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù) : hNamedPipe Long ,指定一個(gè)命名管道的句柄 lpState Long ,用于裝載下述一個(gè)或多個(gè)常數(shù)的長(zhǎng)整數(shù)變量 ???????? PIPE_NOWAIT 管道設(shè)置成永不阻塞,這樣的模式非常少使用 ???????? PIPE_READMODE_MESSAGE 管道設(shè)置成讀取消息 lpCurInstances Long ,裝載這個(gè)管道眼下存在的實(shí)例數(shù)量 lpMaxCollectionCount Long ,如管道設(shè)置成通過(guò)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)傳輸,就用這個(gè)變量裝載通過(guò)管道發(fā)送之前可排隊(duì)等候的最大數(shù)據(jù)量 lpCollectDataTimeout Long ,如管道設(shè)置成通過(guò)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)傳輸,就在這里指定一個(gè)長(zhǎng)整數(shù)變量,用它裝載進(jìn)行一次網(wǎng)絡(luò)數(shù)據(jù)傳輸前須要等候的最長(zhǎng)時(shí)間 lpUserName String ,如這是個(gè)server句柄,就在這里指定一個(gè)字串緩沖區(qū),在當(dāng)中加載客戶應(yīng)用程序的username 。可設(shè)為 vbNullString ,表示不取回信息 nMaxUserNameSize Long ,指定 lpUserName 緩沖區(qū)的長(zhǎng)度,能夠?yàn)榱?/span>
GetNamedPipeInfo 函數(shù) 函數(shù)原型 : BOOL GetNamedPipeInfo(HANDLE hNamedPipe,????? LPDWORD lpFlags, LPDWORD lpOutBufferSize, LPDWORD lpInBufferSize, LPDWORD lpMaxInstances); 說(shuō)明 :獲得指定命名管道的信息 返回值 :假設(shè)函數(shù)運(yùn)行成功,返回值非零,否則,返回值為零,此時(shí)調(diào)用 GetLastError 函數(shù)獲得擴(kuò)展錯(cuò)誤信息 參數(shù) : hNamedPipe :命名管道句柄。這個(gè)句柄具有命名管道的 GENERIC_READ 訪問(wèn)權(quán)限。 lpFlags :指定一個(gè)識(shí)別命名管道類型的 32 位變量。假設(shè)這個(gè)信息不需獲得,能夠給此參數(shù)置 NULL 。否則使用下面的值: 1.??????? PIPE_CLIENT_END 這個(gè)句柄是關(guān)于一個(gè)命名管道的client,此值被默認(rèn) 2.??????? PIPE_SERVER_END 這個(gè)句柄是關(guān)于命名管道的server端。假設(shè)這個(gè)值沒(méi)有被指定,這個(gè)命名管道句柄是關(guān)于client的 3.??????? PIPE_TYPE_BYTE 命名管道是一個(gè)字節(jié)管道型,此值被默認(rèn) 4.??????? PIPE_TYPE_MESSAGE 命名管道是一個(gè)消息管道。假設(shè)這個(gè)值沒(méi)有被指定,則默覺(jué)得字節(jié)管道型 lpOutBufferSize :一個(gè) 32 變量地址。用來(lái)按字節(jié),返回輸出數(shù)據(jù)緩沖的尺寸 。假設(shè)緩沖值為零,則這個(gè)緩沖區(qū)沒(méi)有按要求分配。假設(shè)鎮(zhèn)魂歌信息不須要的,能夠被置 NULL. lpInBufferSize :一個(gè) 32 變量地址。用來(lái)按字節(jié),返回輸入數(shù)據(jù)緩沖的尺寸 。假設(shè)緩沖值為零,則這個(gè)緩沖區(qū)沒(méi)有按要求分配。假設(shè)這個(gè)信息不須要的,能夠被置 NULL. lpMaxInstances :一個(gè) 32 變量地址。用來(lái)獲得被創(chuàng)建的管道實(shí)例的最大尺寸。假設(shè)此位被設(shè)置為 PIPE_UNLIMITED_INSTANCES, 被創(chuàng)建的管道實(shí)例的最大尺寸被依照系統(tǒng)的可容量所限制。假設(shè)這個(gè)信息不須要的,能夠被置 NULL.
PeekNamedPipe 函數(shù) 函數(shù)原型 : BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer,DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage ); 說(shuō)明 :? 預(yù)覽一個(gè)管道中的數(shù)據(jù),或取得與管道中的數(shù)據(jù)有關(guān)的信息 返回值 : ? BOOL ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù) : hNamedPipe ?HANDLE ,指定一個(gè)管道的句柄。這并不一定是某個(gè)命名管道的句柄 —— 匿名管道相同適用 lpBuffer ?LPVOID ,指定要裝載數(shù)據(jù)的一個(gè)緩沖區(qū)的頭一個(gè)字符。能夠?yàn)榱?/span> nBufferSize ?DWORD , lpBuffer 緩沖區(qū)長(zhǎng)度 lpBytesRead? LPDWORD ,保存裝載到緩沖區(qū)的字符數(shù)量 lpTotalBytesAvail LPDWORD ,保存管道中可用的字符數(shù)量 lpBytesLeftThisMessage Long ,保存這次讀操作后仍然保留在消息中的字符數(shù)。僅僅能為那些基于消息的命名管道設(shè)置 注解 : 由這個(gè)函數(shù)讀入的數(shù)據(jù)實(shí)際并不能從管道中刪除。假設(shè)要對(duì)一個(gè)管道進(jìn)行輪詢,了解是否有可能數(shù)據(jù),那么使用這個(gè)函數(shù)特別理想
SetNamedPipeHandleState 函數(shù) 函數(shù)原型 : BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout ); 說(shuō)明 : 設(shè)置與一個(gè)命名管道的運(yùn)作有關(guān)的信息 返回值值 : BOOL ,非零表示成功,零表示失敗。會(huì)設(shè)置 GetLastError 參數(shù): hNamedPipe HANDLE ,指定一個(gè)命名管道的句柄 lpMode LPDWORD ,下列常數(shù)的一個(gè)或多個(gè): PIPE_WAIT , PIPE_NOWAIT , PIPE_READMODE_BYTE 以及 PIPE_READMODE_MESSAGE 。請(qǐng)參考 CreateNamedPipe 函數(shù),了解有關(guān)這些標(biāo)志的進(jìn)一步情況 pMaxCollectionCount LPDWORD ,如管道設(shè)為通過(guò)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)傳輸,則在這里指定通過(guò)管道發(fā)送之前可排除等候的最大數(shù)據(jù)量 lpCollectDataTimeout LPDWORD ,如管道設(shè)為通過(guò)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)傳輸,則在這里指定網(wǎng)絡(luò)數(shù)據(jù)傳輸前可以忍受的最長(zhǎng)等候時(shí)間(超時(shí))
TransactNamedPipe 函數(shù) 函數(shù)原型 : BOOL TransactNamedPipe( HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, LPOVERLAPPED lpOverlapped ); 說(shuō)明 : 該函數(shù)在單獨(dú)一個(gè)函數(shù)中同一時(shí)候合并了對(duì)管道的讀、寫(xiě)操作。客戶和server進(jìn)程都可用它 返回值 : BOOL ,如操作已結(jié)束,則返回 TRUE( 非零 ); 否則返回零。在異步模式中, GetLastError 會(huì)設(shè)置成 ERROR_IO_PENDING ,并且操作會(huì)繼續(xù)在后臺(tái)進(jìn)行。可測(cè)試 lpOverlapped 結(jié)構(gòu)中的事件對(duì)象,了解操作是否結(jié)束 參數(shù) : hNamedPipe HANDLE ,指定一個(gè)消息類型的命名管道的句柄 lpInBuffer LPVOID ,指定一個(gè)內(nèi)存緩沖區(qū),在當(dāng)中包括要寫(xiě)入管道的數(shù)據(jù) nInBufferSize DWORD ,指定 lpInBuffer 緩沖區(qū)中的字節(jié)數(shù)量 lpOutBuffer LPVOID ,指定一個(gè)內(nèi)存緩沖區(qū),用于裝載從管道中讀入的數(shù)據(jù) nOutBufferSize DWORD ,用于裝載來(lái)自管道的數(shù)據(jù) lpBytesRead LPDWORD ,指定要從管道讀入的字節(jié)數(shù)量。會(huì)讀入單條消息。如因?yàn)?/span>lpOutBuffer 不夠大,不能容下完整的消息,那么函數(shù)會(huì)返回 FALSE ,并且 GetLastError 會(huì)設(shè)為 ERROR_MORE_DATA (消息中剩下的全部字節(jié)都會(huì)丟失) lpOverlapped LPOVERLAPPED ,能夠?yàn)?/span>NULL (變成 ByVal As Long ,并傳遞零值),或指定包括了一個(gè)事件對(duì)象的 OVERLAPPED 結(jié)構(gòu) 注解 : 如 lpOverlapped 設(shè)為 NULL ,或者句柄沒(méi)有創(chuàng)建成 FILE_FLAG_OVERLAPPED 樣式,那么除非讀和寫(xiě)操作都完畢,否則函數(shù)不會(huì)返回
WaitNamedPipe 函數(shù) 函數(shù)原型 : BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName, DWORD nTimeOut ); 說(shuō)明: 由一個(gè)客戶進(jìn)程調(diào)用, 等候一個(gè)管道變成可用狀態(tài) (比方server已調(diào)用 ConnectNamedPipe 函數(shù)同一個(gè)客戶連接) 返回值: BOOL ,非零表示成功;假設(shè)失敗,或者管道不存在,則返回零。會(huì)設(shè)置 GetLastError 參數(shù) : lpNamedPipeName ?LPCTSTR ,指定要連接的管道名稱 nTimeOut ?DWORD ,以毫秒數(shù)表示的等待時(shí)間,或者下述常數(shù)之中的一個(gè): 1.????? NMPWAIT_USE_DEFAULT_WAIT 使用管道創(chuàng)建時(shí)的默認(rèn)超時(shí)設(shè)定 2.???? NMPWAIT_WAIT_FOREVER 永遠(yuǎn)等待 注解: 在這個(gè)函數(shù)之后,用 CreateFile 打開(kāi)管道。注意從這個(gè)函數(shù)返回到調(diào)用 CreateFile 函數(shù)期間,倘若有還有一個(gè)進(jìn)程同管道連接,那么這個(gè) CreateFile 調(diào)用可能失敗
?
二、
?
BOOL WaitNamedPipe( LPCTSTR lpNamedPipeName, DWORD nTimeOut );
?
?lpNamedPipeName 要打開(kāi)的管道名,格式\\servername\pipe\pipename,假設(shè)是本地管道則servername能夠使用點(diǎn)“.”。
nTimeOut 等待命名管道的一個(gè)實(shí)例有效的超時(shí)時(shí)間,單位毫秒,也能夠使用以下兩個(gè)值中的一個(gè):
?? NMPWAIT_USE_DEFAULT_WAIT 0x00000000,使用服務(wù)端CreateNamedPipe 創(chuàng)建管道時(shí)設(shè)置的超時(shí)時(shí)間。
?? NMPWAIT_WAIT_FOREVER 0xffffffff,一直等到一個(gè)命名管道的實(shí)例有效才返回。
返回值:
假設(shè)在超時(shí)時(shí)間前管道的一個(gè)實(shí)例有效,返回非0。
假設(shè)超時(shí)時(shí)間內(nèi)沒(méi)有一個(gè)有效的實(shí)例,返回0。
注意:
假設(shè)指定的命名管道沒(méi)有實(shí)例存在,即沒(méi)有服務(wù)端創(chuàng)建該命名管道(所以在確定server端創(chuàng)建了該命名管道時(shí)能夠不調(diào)用此API) ,函數(shù)無(wú)視超時(shí)等待時(shí)間直接返回0 。
假設(shè)函數(shù)運(yùn)行成功返回TRUE,表示至少有一個(gè)命名管道的實(shí)例有效,接下來(lái)應(yīng)該使用CreateFile函數(shù)打開(kāi)命名管道的一個(gè)句柄,可是CreateFile可能會(huì)打開(kāi)管道失敗,由于該實(shí)例有可能被服務(wù)端關(guān)閉或被已經(jīng)被其它c(diǎn)lient打開(kāi)。
?
三、管道使用的傷痛所在 平時(shí)都使用得好好的管道突然出發(fā)問(wèn)題了 static inline BOOL
CTPipe_WriteNBytes(HANDLE hPipe, BYTE *buf, DWORD size, DWORD timeout, HANDLE stopEvent)
{BOOL ret = FALSE;BOOL writeRet;OVERLAPPED ol;BYTE *pos;DWORD cbBytesWrite;HANDLE hWriteEvent;memset( &ol, 0, sizeof(ol) );ol.hEvent = hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if ( ol.hEvent == NULL ){return FALSE;}pos = buf;while ( size > 0 ){writeRet = WriteFile(hPipe, pos,size,&cbBytesWrite,&ol);if ( writeRet ){pos += cbBytesWrite;size -= cbBytesWrite;ResetEvent(hWriteEvent);}else{if ( GetLastError() == ERROR_IO_PENDING ){DWORD waitRet;HANDLE handles[2];int numOfHandles = 1;handles[0] = hWriteEvent;if ( stopEvent ){handles[1] = stopEvent;numOfHandles++;}waitRet = WaitForMultipleObjects(numOfHandles, handles, FALSE, timeout);if ( waitRet == WAIT_OBJECT_0 ){writeRet = GetOverlappedResult(hPipe, &ol, &cbBytesWrite, TRUE );if ( writeRet == FALSE ){goto EXIT;}pos += cbBytesWrite;size -= cbBytesWrite;ResetEvent(hWriteEvent);}else /* timeout, notify event or other */{/** Cancel request*/if ( CancelIo(hPipe) == FALSE ){L_ERROR(_T("IoCancel fail 0x%x, force close pipe\n"), GetLastError());CloseHandle(hPipe);}else{/** Wait cannel finish*/GetOverlappedResult(hPipe, &ol, &cbBytesWrite, TRUE);}goto EXIT;}}else{goto EXIT;}}}ret = TRUE;EXIT:if ( hWriteEvent ){CloseHandle(hWriteEvent);}return ret;
}
在waitRet = WaitForMultipleObjects(numOfHandles, handles, FALSE, timeout);發(fā)生了堵塞,此管理是採(cǎi)用異步的,這里寫(xiě)的管道是作為服務(wù)端,問(wèn)題是服務(wù)端的代碼根本就沒(méi)有變,倒是管道client的代碼變了。 在client是起一個(gè)線程不斷地讀管道的數(shù)據(jù),可是讀到數(shù)據(jù)后是有一個(gè)處理數(shù)據(jù)的過(guò)程。 而在曾經(jīng)的版本號(hào)呻這個(gè)過(guò)程僅僅是PostMessage一個(gè)消息,就繼續(xù)去ReadFile了;而在新的版本號(hào)中讀到數(shù)據(jù)卻是花了非常多時(shí)間去處理數(shù)據(jù),然后才去ReadFile。 導(dǎo)致服務(wù)端管道寫(xiě)堵塞的原因:
我們?cè)O(shè)置的模式是PIPE_WAIT,依據(jù)MSDN的要求是:當(dāng)一端在寫(xiě)時(shí),還有一要發(fā)生讀的動(dòng)作,才干寫(xiě)成功;否則堵塞。 hPipe = CreateNamedPipe("Name",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,PIPE_UNLIMITED_INSTANCES,0,0,0, /*XENHANCE_PIPE_TIMEOUT*/&sa);
解決方法 :
法一、在服務(wù)端的管道寫(xiě)時(shí)設(shè)置超時(shí)時(shí)間,這盡管能使用服務(wù)端管道的線程不卡死,但也可能該寫(xiě)的數(shù)據(jù)沒(méi)有寫(xiě)進(jìn)去。 法二、依據(jù)上圖所看到的,去掉“管道Client端“中的”等待RetEvent事件。 缺點(diǎn):“管道Client端“不知道自己要求”管道Service端“運(yùn)行的動(dòng)作的結(jié)果。
1、
2、
3、
4、
5、
6、
7、
轉(zhuǎn)載于:https://www.cnblogs.com/zfyouxi/p/4210098.html
總結(jié)
以上是生活随笔 為你收集整理的管道实现进程间通讯 、WaitNamedPipe 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。