? 一、管道實現進程間通訊 主要的理論知識 1.什么是管道以及分類 管道是兩個頭的東西,每一個頭各連接一個進程或者同一個進程的不同代碼 ,依照管道的類別分有兩種管道,匿名的和命名的;依照管道的傳輸方向分也能夠分成兩種,單向的雙向的 。依據管道的特點,命名管道通經常使用在網絡環境下不同計算機上執行的進程之間的通信(當然也能夠用在同一臺機的不同進程中)它能夠是單向或雙向的;而匿名管道僅僅能用在同一臺計算機中,它僅僅能是單向的。匿名管道事實上是通過用給了一個指定名字的有名管道來實現的 。 使用管道的優點在于:讀寫它使用的是對文件操作的 api ,結果操作管道就和操作文件一樣。即使你在不同的計算機之間用命名管道來通信,你也不必了解和自己去實現網絡間通信的詳細細節。 2.管道的使用 A.命名管道 命名管道是由server端的進程建立的,管道的命名必須遵循特定的命名方法,就是 "//./pipe/ 管道名 " ,當作為client的進程要使用時,使用 "// 計算機名 //pipe/ 管道名 " 來打開使用,詳細過程例如以下:
服務端通過函數 CreateNamedPipe 創建一個命名管道的實例并返回用于今后操作的句柄,或為已存在的管道創建新的實例。服務端偵聽來自client的連接請求,該功能通過 ConnectNamedPipe 函數實現。
client通過函數 WaitNamedPipe 來等待管道的出現,假設在超時值變為零曾經,有一個管道能夠使用 ,則 WaitNamedPipe 將返回 True ,并通過調用 CreateFile 或 CallNamedPipe 來呼叫對服務端的連接。
此時服務端將接受client的連接請求,成功建立連接,服務端 ConnectNamedPipe 返回 True 建立連接之后,client與server端就可以通過 ReadFile 和 WriteFile ,利用得到的管道文件句柄,彼此間進行信息交換。 當client與服務端的通信結束,client調用 CloseFile ,服務端接著調用 DisconnectNamedPipe 。最后調用函數 CloseHandle 來關閉該管道。
B.匿名管道 因為命名管道使用時作為client的程序必須知道管道的名稱,所以很多其它的用在同一 “ 作者 ” 編寫的server / 工作站程序中,你不可能隨便找出一個程序來要求它和你寫的程序來通過命名管道通信。而匿名管道的使用則全然不同,它同意你和全然不相干的進程通信,條件是這個進程通過控制臺 “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 來關閉兩個管道。 ? 管道使用的API函數集
CallNamedPipe 函數 函數原型: BOOL CallNamedPipe( LPCTSTR lpNamedPipeName, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, DWORD nTimeOut ); 說明 : 這個函數由一個希望通過管道通信的一個客戶進程調用。如有可能,它就同一個管道連接(在必要的情況下等候管道可用)。隨后,它對指定的數據進行讀寫,然后將管道關閉 返回值 : Long ,非零表示成功,零表示失敗。會設置 GetLastError 參數表 : lpNamedPipeName: LPCTSTR ,指定管道名,採用的形式是: //./ 管道 / 管道名。最多可達 256 個字符的長度,并且不用區分大寫和小寫。假設存在指定名字的一個管道,則創建那個管道的一個新實例 lpInBuffer: LPVOID ,包括了要寫入管道的數據的一個內存緩沖區 nInBufferSize: DWORD , lpInBuffer 緩沖區中的字符數量 lpOutBuffer : LPVOID , 指定一個內存緩沖區,用于裝載從管道中讀出的數據 nOutBufferSize : DWORD , 指定一個長整數變量,用于裝載來自管道的數據 lpBytesRead : LPDWORD , 指定從管道中讀出的字節數。會閱讀單條消息。如 lpOutBuffer 的容量不夠大,不能容下整條消息,則函數會返回 FALSE ,并且 GetLastError 會設為 ERROR_MORE_DATA (消息中留下的不論什么字節都會丟失) nTimeOut : DWORD ,下列常量之中的一個: 1.??????? NMPWAIT_NOWAIT : ? 如管道不可用,則馬上返回一個錯誤 2.??????? NMPWAIT_WAIT_FOREVER : 永遠等候管道可用 . 3.??????? NMPWAIT_USE _DEFAULT_WAIT : 使用管道的默認超時設置,這個設置是用 CreateNamedPipe 函數指定的
ConnectNamedPipe 函數 函數原型: BOOL ConnectNamedPipe( HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped ); 說明: 指示一臺server等待下去,直至客戶機同一個命名管道連接 返回值: BOOL ,如 lpOverlapped 為 NULL ,那么: 1.????? 如管道已連接,就返回 Ture (非零); ????? 如錯誤發生,或者管道已經連接,就返回零( GetLastError 此時會返回 ERROR_PIPE_CONNECTED ) 2.???? lpOverlapped 有效,就返回零; ???????如管道已經連接, GetLastError 會返回 ERROR_PIPE_CONNECTED ; ????? 如重疊操作成功完畢,就返回 ERROR_IO_PENDING 。 ???????在這兩種情況下,倘若一個客戶已關閉了管道,且server尚未用 DisconnectNamedPipe 函數同客戶斷開連接,那么 GetLastError 都會返回 ERROR_NO_DATA 參數 : hNamedPipe : HANDLE ,管道的句柄 lpOverlapped : LPOVERLAPPED , ??????? 如設為 NULL (傳遞 ByVal As Long ),表示將線程掛起,直到一個客戶同管道連接為止。否則就馬上返回; ???? 此時,如管道尚未連接,客戶同管道連接時就會觸發 lpOverlapped 結構中的事件對象。隨后,可用一個等待函數來監視連接 適用平臺 : Windows NT 凝視: 可用這個函數將一個管道換成同還有一個客戶連接,但首先必須用 DisconnectNamedPipe 函數斷開同當前進程的連接
CreateNamedPipe 函數 函數原型: HANDLE CreateNamedPipe( LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes); 說明: 創建一個命名管道。返回的句柄由管道的server端使用 返回值: HANDLE ,如運行成功,返回管道的句柄。 INVALID_HANDLE_VALUE 表示失敗 . 會設置 GetLastError 參數: lpName : LPCTSTR ,指定管道名,採用的形式是: //./ 管道 / 管道名 。最多可達 256 個字符的長度,并且不用區分大寫和小寫。假設存在指定名字的一個管道,則創建那個管道的一個新實例 dwOpenMode : DWORD , 下述常數組的一個組合 下述常數之中的一個(對于管道的全部實例都要一樣): 1.????????? PIPE_ACCESS_DUPLEX 管道是雙向的 2.????????? PIPE_ACCESS_INBOUND 數據從client流到server端 3.????????? PIPE_ACCESS_OUTBOUND 數據從server端流到client 下述常數的隨意組合 1.????????? FILE_FLAG_WRITE_THROUGH 在網絡中建立的字節型管道內,強迫數據在每次讀寫操作的時候通過網絡傳輸。否則傳輸就可能延遲 2.????????? FILE_FLAG_OVERLAPPED 同意(但不要求)用這個管道進行異步(重疊式)操作 常數 WRITE_DAC , WRITE_OWNER 和 ACCESS_ SYSTEM_SECURITY 提供了附加的安全選項 dwPipeMode : DWORD ,下述常數組的一個組合 下述常數之中的一個(管道的全部實例都必須指定同樣的常數) 1.????????? PIPE_TYPE_BYTE 數據作為一個連續的字節數據流寫入管道 2.????????? PIPE_TYPE_MESSAGE 數據用數據塊(名為 “ 消息 ” 或 “ 報文 ” )的形式寫入管道 下述常數之中的一個: 1.????????? PIPE_READMODE_PIPE 數據以單獨字節的形式從管道中讀出 2.????????? PIPE_READMODE_MESSAGE 數據以名為 “ 消息 ” 的數據塊形式從管道中讀出(要求指定 PIPE_TYPE_MESSAGE ) 下述常數之中的一個: 1.????????? PIPE_WAIT 同步操作在等待的時候掛起線程 2.????????? PIPE_NOWAIT (不推薦!) 同步操作馬上返回。這樣可為異步傳輸提供一種落后的實現方法,已由 Win32 的重疊式傳輸機制代替了 nMaxInstances : DWORD ,這個管道可以創建的最大實例數 量。必須是 1 到常數 PIPE_UNLIMITED_INSTANCES 間的一個值。它對于管道的全部實例來說都應是同樣的 nOutBufferSize : DWORD ,建議的輸出緩沖區長度;零表示用默認設置 nInBufferSize : DWORD ,建議的輸入緩沖區長度;零表示用默認設置 nDefaultTimeOut : DWORD ,管道的默認等待超時 。對一個管道的全部實例來說都應同樣 lpSecurityAttributes : LPSECURITY_ATTRIBUTES ,指定一個 SECURITY_ATTRIBUTES 結構,或者傳遞零值(將參數聲明為 ByVal As Long ,并傳遞零值),以便使用不同意繼承的一個默認描寫敘述符 適用平臺: Windows NT
CreatePipe 函數 函數原型: BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize ); 說明: 創建一個匿名管道 返回值: Long ,非零表示成功,零表示失敗。會設置 GetLastError 參數 : phReadPipe : PHANDLE ,指定一個變量,設為管道讀入(輸出)端的一個句柄 phWritePipe : PHANDLE ,指定一個變量,設為管道寫入(輸入)端的一個句柄 lpPipeAttributes : LPSECURITY _ATTRIBUTES ,指定一個 SECURITY_ATTRIBUTES 結構,或者傳遞零值,以便使用不同意繼承的一個默認描寫敘述符 nSize : DWORD ,管道緩沖區的建議大小。零表示用默認值 注解: 匿名管道不同意異步操作,所以如在一個管道中寫入數據,且緩沖區已滿,那么除非還有一個進程從管道中讀出數據,從而騰出了緩沖區的空間,否則寫入函數不會返回
DisconnectNamedPipe 函數 函數原型: BOOL DisconnectNamedPipe( HANDLE hNamedPipe ); 說明: 斷開一個客戶與一個命名管道的連接(server端與client都可調用) 返回值: BOOL ,非零表示成功,零表示失敗。會設置 GetLastError 參數: hNamedPipe Long ,管道的句柄 適用平臺: Windows NT 注解 :如客戶尚未在它自己那端關閉管道句柄,下次試圖訪問管道的時候就會錯誤發生
GetNamedPipeHandleState 函數 函數原型: BOOL GetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpState, LPDWORD lpCurInstances, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout, LPTSTR lpUserName, DWORD nMaxUserNameSize); 說明 : 獲取一個命名管道當前的狀態信息 返回值 : BOOL ,非零表示成功,零表示失敗。會設置 GetLastError 參數 : hNamedPipe Long ,指定一個命名管道的句柄 lpState Long ,用于裝載下述一個或多個常數的長整數變量 ???????? PIPE_NOWAIT 管道設置成永不阻塞,這樣的模式非常少使用 ???????? PIPE_READMODE_MESSAGE 管道設置成讀取消息 lpCurInstances Long ,裝載這個管道眼下存在的實例數量 lpMaxCollectionCount Long ,如管道設置成通過一個網絡數據傳輸,就用這個變量裝載通過管道發送之前可排隊等候的最大數據量 lpCollectDataTimeout Long ,如管道設置成通過一個網絡數據傳輸,就在這里指定一個長整數變量,用它裝載進行一次網絡數據傳輸前須要等候的最長時間 lpUserName String ,如這是個server句柄,就在這里指定一個字串緩沖區,在當中加載客戶應用程序的username 。可設為 vbNullString ,表示不取回信息 nMaxUserNameSize Long ,指定 lpUserName 緩沖區的長度,能夠為零
GetNamedPipeInfo 函數 函數原型 : BOOL GetNamedPipeInfo(HANDLE hNamedPipe,????? LPDWORD lpFlags, LPDWORD lpOutBufferSize, LPDWORD lpInBufferSize, LPDWORD lpMaxInstances); 說明 :獲得指定命名管道的信息 返回值 :假設函數運行成功,返回值非零,否則,返回值為零,此時調用 GetLastError 函數獲得擴展錯誤信息 參數 : hNamedPipe :命名管道句柄。這個句柄具有命名管道的 GENERIC_READ 訪問權限。 lpFlags :指定一個識別命名管道類型的 32 位變量。假設這個信息不需獲得,能夠給此參數置 NULL 。否則使用下面的值: 1.??????? PIPE_CLIENT_END 這個句柄是關于一個命名管道的client,此值被默認 2.??????? PIPE_SERVER_END 這個句柄是關于命名管道的server端。假設這個值沒有被指定,這個命名管道句柄是關于client的 3.??????? PIPE_TYPE_BYTE 命名管道是一個字節管道型,此值被默認 4.??????? PIPE_TYPE_MESSAGE 命名管道是一個消息管道。假設這個值沒有被指定,則默覺得字節管道型 lpOutBufferSize :一個 32 變量地址。用來按字節,返回輸出數據緩沖的尺寸 。假設緩沖值為零,則這個緩沖區沒有按要求分配。假設鎮魂歌信息不須要的,能夠被置 NULL. lpInBufferSize :一個 32 變量地址。用來按字節,返回輸入數據緩沖的尺寸 。假設緩沖值為零,則這個緩沖區沒有按要求分配。假設這個信息不須要的,能夠被置 NULL. lpMaxInstances :一個 32 變量地址。用來獲得被創建的管道實例的最大尺寸。假設此位被設置為 PIPE_UNLIMITED_INSTANCES, 被創建的管道實例的最大尺寸被依照系統的可容量所限制。假設這個信息不須要的,能夠被置 NULL.
PeekNamedPipe 函數 函數原型 : BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer,DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage ); 說明 :? 預覽一個管道中的數據,或取得與管道中的數據有關的信息 返回值 : ? BOOL ,非零表示成功,零表示失敗。會設置 GetLastError 參數 : hNamedPipe ?HANDLE ,指定一個管道的句柄。這并不一定是某個命名管道的句柄 —— 匿名管道相同適用 lpBuffer ?LPVOID ,指定要裝載數據的一個緩沖區的頭一個字符。能夠為零 nBufferSize ?DWORD , lpBuffer 緩沖區長度 lpBytesRead? LPDWORD ,保存裝載到緩沖區的字符數量 lpTotalBytesAvail LPDWORD ,保存管道中可用的字符數量 lpBytesLeftThisMessage Long ,保存這次讀操作后仍然保留在消息中的字符數。僅僅能為那些基于消息的命名管道設置 注解 : 由這個函數讀入的數據實際并不能從管道中刪除。假設要對一個管道進行輪詢,了解是否有可能數據,那么使用這個函數特別理想
SetNamedPipeHandleState 函數 函數原型 : BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout ); 說明 : 設置與一個命名管道的運作有關的信息 返回值值 : BOOL ,非零表示成功,零表示失敗。會設置 GetLastError 參數: hNamedPipe HANDLE ,指定一個命名管道的句柄 lpMode LPDWORD ,下列常數的一個或多個: PIPE_WAIT , PIPE_NOWAIT , PIPE_READMODE_BYTE 以及 PIPE_READMODE_MESSAGE 。請參考 CreateNamedPipe 函數,了解有關這些標志的進一步情況 pMaxCollectionCount LPDWORD ,如管道設為通過一個網絡數據傳輸,則在這里指定通過管道發送之前可排除等候的最大數據量 lpCollectDataTimeout LPDWORD ,如管道設為通過一個網絡數據傳輸,則在這里指定網絡數據傳輸前可以忍受的最長等候時間(超時)
TransactNamedPipe 函數 函數原型 : BOOL TransactNamedPipe( HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, LPOVERLAPPED lpOverlapped ); 說明 : 該函數在單獨一個函數中同一時候合并了對管道的讀、寫操作。客戶和server進程都可用它 返回值 : BOOL ,如操作已結束,則返回 TRUE( 非零 ); 否則返回零。在異步模式中, GetLastError 會設置成 ERROR_IO_PENDING ,并且操作會繼續在后臺進行。可測試 lpOverlapped 結構中的事件對象,了解操作是否結束 參數 : hNamedPipe HANDLE ,指定一個消息類型的命名管道的句柄 lpInBuffer LPVOID ,指定一個內存緩沖區,在當中包括要寫入管道的數據 nInBufferSize DWORD ,指定 lpInBuffer 緩沖區中的字節數量 lpOutBuffer LPVOID ,指定一個內存緩沖區,用于裝載從管道中讀入的數據 nOutBufferSize DWORD ,用于裝載來自管道的數據 lpBytesRead LPDWORD ,指定要從管道讀入的字節數量。會讀入單條消息。如因為 lpOutBuffer 不夠大,不能容下完整的消息,那么函數會返回 FALSE ,并且 GetLastError 會設為 ERROR_MORE_DATA (消息中剩下的全部字節都會丟失) lpOverlapped LPOVERLAPPED ,能夠為 NULL (變成 ByVal As Long ,并傳遞零值),或指定包括了一個事件對象的 OVERLAPPED 結構 注解 : 如 lpOverlapped 設為 NULL ,或者句柄沒有創建成 FILE_FLAG_OVERLAPPED 樣式,那么除非讀和寫操作都完畢,否則函數不會返回
WaitNamedPipe 函數 函數原型 : BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName, DWORD nTimeOut ); 說明: 由一個客戶進程調用, 等候一個管道變成可用狀態 (比方server已調用 ConnectNamedPipe 函數同一個客戶連接) 返回值: BOOL ,非零表示成功;假設失敗,或者管道不存在,則返回零。會設置 GetLastError 參數 : lpNamedPipeName ?LPCTSTR ,指定要連接的管道名稱 nTimeOut ?DWORD ,以毫秒數表示的等待時間,或者下述常數之中的一個: 1.????? NMPWAIT_USE_DEFAULT_WAIT 使用管道創建時的默認超時設定 2.???? NMPWAIT_WAIT_FOREVER 永遠等待 注解: 在這個函數之后,用 CreateFile 打開管道。注意從這個函數返回到調用 CreateFile 函數期間,倘若有還有一個進程同管道連接,那么這個 CreateFile 調用可能失敗
?
二、
?
BOOL WaitNamedPipe( LPCTSTR lpNamedPipeName, DWORD nTimeOut );
?
?lpNamedPipeName 要打開的管道名,格式\\servername\pipe\pipename,假設是本地管道則servername能夠使用點“.”。
nTimeOut 等待命名管道的一個實例有效的超時時間,單位毫秒,也能夠使用以下兩個值中的一個:
?? NMPWAIT_USE_DEFAULT_WAIT 0x00000000,使用服務端CreateNamedPipe 創建管道時設置的超時時間。
?? NMPWAIT_WAIT_FOREVER 0xffffffff,一直等到一個命名管道的實例有效才返回。
返回值:
假設在超時時間前管道的一個實例有效,返回非0。
假設超時時間內沒有一個有效的實例,返回0。
注意:
假設指定的命名管道沒有實例存在,即沒有服務端創建該命名管道(所以在確定server端創建了該命名管道時能夠不調用此API) ,函數無視超時等待時間直接返回0 。
假設函數運行成功返回TRUE,表示至少有一個命名管道的實例有效,接下來應該使用CreateFile函數打開命名管道的一個句柄,可是CreateFile可能會打開管道失敗,由于該實例有可能被服務端關閉或被已經被其它client打開。
?
三、管道使用的傷痛所在 平時都使用得好好的管道突然出發問題了 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);發生了堵塞,此管理是採用異步的,這里寫的管道是作為服務端,問題是服務端的代碼根本就沒有變,倒是管道client的代碼變了。 在client是起一個線程不斷地讀管道的數據,可是讀到數據后是有一個處理數據的過程。 而在曾經的版本號呻這個過程僅僅是PostMessage一個消息,就繼續去ReadFile了;而在新的版本號中讀到數據卻是花了非常多時間去處理數據,然后才去ReadFile。 導致服務端管道寫堵塞的原因:
我們設置的模式是PIPE_WAIT,依據MSDN的要求是:當一端在寫時,還有一要發生讀的動作,才干寫成功;否則堵塞。 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);
解決方法 :
法一、在服務端的管道寫時設置超時時間,這盡管能使用服務端管道的線程不卡死,但也可能該寫的數據沒有寫進去。 法二、依據上圖所看到的,去掉“管道Client端“中的”等待RetEvent事件。 缺點:“管道Client端“不知道自己要求”管道Service端“運行的動作的結果。
1、
2、
3、
4、
5、
6、
7、
轉載于:https://www.cnblogs.com/zfyouxi/p/4210098.html
總結
以上是生活随笔 為你收集整理的管道实现进程间通讯 、WaitNamedPipe 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。