1. 程式人生 > >程序通訊之二 管道技術第三篇 命名管道

程序通訊之二 管道技術第三篇 命名管道

               

上一篇《程序通訊之二管道技術第二篇匿名管道》中講解了匿名管道,匿名管道有讀取端和寫入端,在建立匿名管道(CreatePipe)後就可以像讀寫檔案一樣的對管道中進行讀寫(ReadFileWriteFile,注意讀寫順序)。在關閉匿名管道兩端後會由系統負責銷燬並回收資源。文章中還示範了父程序如何使用匿名管道來改變子程序的輸入輸出。

本篇將講解管道技術中的命名管道(Named Pipes),顧名思義,這個管道肯定是有名字的,聯想到中的事件互斥量訊號量(見附1,它們的名字主要是用於確保多個程序訪問同一個物件。因此肯定也可以通過管道的名字來確保多個程序訪問同一個管道。事實上,命名管道不僅可在同一臺計算機的不同程序之間傳輸資料,甚至能在跨越一個網路的不同計算機的不同程序之間,支援可靠的、單向或雙向的資料通訊。

先來看看如何建立和使用命名管道。

第一個CreateNamedPipe

函式功能:建立命名管道

函式原型:

HANDLEWINAPICreateNamedPipe(

LPCTSTRlpName,

DWORDdwOpenMode,

DWORDdwPipeMode,

DWORDnMaxInstances,

DWORDnOutBufferSize,

DWORDnInBufferSize,

DWORDnDefaultTimeOut,

LPSECURITY_ATTRIBUTESlpSecurityAttributes

);

引數說明:

第一個引數LPCTSTRlpName

表示管道名稱,採用的形式是:\\.\pipe\pipename

。最多可達256個字元的長度,而且不區分大小寫。如果已經有同名管道,則會建立那個管道的一個新例項。

第二個引數DWORDdwOpenMode

表示管道的開啟方式。下面列出最常用的三種,更多請參閱MSDN

1.PIPE_ACCESS_DUPLEX

該管道是雙向的,伺服器和客戶端程序都可以從管道讀取或者向管道寫入資料。

2.PIPE_ACCESS_INBOUND

該管道中資料是從客戶端流向服務端,即客戶端只能寫,服務端只能讀。

3.PIPE_ACCESS_OUTBOUND

該管道中資料是從服務端流向客戶端,即客戶端只能讀,服務端只能寫。

第三個引數DWORDdwPipeMode

表示管道的模式,下面是一些常用模式介紹,更多請參閱

MSDN

1.PIPE_TYPE_BYTE       

資料作為一個連續的位元組資料流寫入管道。

2.PIPE_TYPE_MESSAGE 

資料用資料塊(名為訊息報文)的形式寫入管道。

3.PIPE_READMODE_BYTE

資料以單獨位元組的形式從管道中讀出。

4.PIPE_READMODE_MESSAGE

資料以名為訊息的資料塊形式從管道中讀出(要求指定PIPE_TYPE_MESSAGE)。

5.PIPE_WAIT

同步操作在等待的時候掛起執行緒。

6.PIPE_NOWAIT

同步操作立即返回。

第四個引數DWORDnMaxInstances

表示該管道所能夠建立的最大例項數量。必須是1到常數PIPE_UNLIMITED_INSTANCES間的一個值。

WINBASE.H中有#define PIPE_UNLIMITED_INSTANCES255

第五個引數DWORDnOutBufferSize

表示管道的輸出緩衝區容量,為0表示使用預設大小。

第六個引數DWORDnInBufferSize

表示管道的輸入緩衝區容量,為0表示使用預設大小。

第七個引數DWORDnDefaultTimeOut

表示管道的預設等待超時。

第八個引數LPSECURITY_ATTRIBUTESlpSecurityAttributes

表示管道的安全屬性

函式返回值:

函式執行成功返回命名管道的控制代碼,否則返回INVALID_HANDLE_VALUE

第二個ConnectNamedPipe

函式功能:等待客戶端連線命名管道

函式原型:

BOOLWINAPIConnectNamedPipe(

HANDLEhNamedPipe,

LPOVERLAPPEDlpOverlapped

);

函式說明:

第一個引數表示命名管道的控制代碼。

第二個引數是一個指向OVERLAPPED結構的指標,一般置為NULL就可以了。

第三個WaitNamedPipe

函式功能:客戶端連線命名管道

函式原型:

BOOLWINAPIWaitNamedPipe(

LPCTSTRlpNamedPipeName,

DWORDnTimeOut

);

函式說明:

第一個引數LPCTSTRlpNamedPipeName

表示管道名稱,採用的形式是:\\servername\pipe\pipename。如果是本機管道,servername用“.”來表示。

第二個引數DWORDnTimeOut

表示等待命名管道的一個例項有效的超時時間,單位毫秒。也可以用NMPWAIT_USE_DEFAULT_WAIT表示使用命名管道的設定值(在呼叫CreateNamedPipe建立命名管道時指定的),NMPWAIT_WAIT_FOREVER表示無限等待。

函式返回值:

在指定時間內連線成功返回TRUE,否則返回FALSE

注意

1如果指定名稱的命名管道還沒建立,函式立即返回,返回值為FALSE

2如果函式執行成功返回TRUE,表示至少有一個命名管道的例項有效,接下來應該使用CreateFile函式開啟命名管道的一個控制代碼,但是CreateFile可能會開啟管道失敗,因為該例項有可能被服務端關閉或被已經被其他客戶端開啟。

下面給出使用命名管道的例項,該例項分為命名管道的服務端和客戶端。服務端和客戶端的主要步驟如下所示:

1. 服務端用CreateNamedPipe建立一個命名管道並使用ConnectNamedPipe等待客戶端的連線。

2. 客戶端使用WaitNamedPipe連線成功後,用CreateFile開啟管道並使用WriteFile管道中寫入一段資料(即向服務端傳送訊息)。

3. 服務端使用ReadFile從管道中讀取資料後(即收到訊息)再向管道中寫入確認資訊表明已經收到客戶端傳輸的資料(即通知客戶端已收到)。

4. 客戶端收到確認資訊後結束,呼叫CloseHandle關閉管道(該管道是CreateFile開啟的)。

5.服務端使用DisconnectNamedPipeCloseHandle關閉管道。

程式碼中的CreateFile,WriteFile,ReadFile請參見上一篇《程序通訊之二管道技術第二篇匿名管道》中的介紹。

服務端程式碼如下:

#include <stdio.h>#include <windows.h>#include <conio.h>const char *pStrPipeName = "\\\\.\\pipe\\NamePipe_MoreWindows";int main()printf("        命名管道 伺服器\n");   printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    printf("建立命名管道並等待連線\n"); HANDLE hPipe = CreateNamedPipe(pStrPipeName, PIPE_ACCESS_DUPLEX,   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,   PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0); if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待連線。 {  printf("連線成功,開始接收資料\n");  const int BUFFER_MAX_LEN = 256;  char szBuffer[BUFFER_MAX_LEN];  DWORD dwLen;  //接收客戶端傳送的資料  ReadFile(hPipe, szBuffer, BUFFER_MAX_LEN, &dwLen, NULL);//讀取管道中的內容(管道是一種特殊的檔案)  printf("接收到資料長度為%d位元組\n", dwLen);  printf("具體資料內容如下:%s\n", szBuffer);  //確認已收到資料  printf("向客戶端傳送已經收到標誌\n");  strcpy(szBuffer, "伺服器已經收到");  WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &dwLen, NULL); } DisconnectNamedPipe(hPipe);  CloseHandle(hPipe);//關閉管道 return 0;}

客戶端程式碼如下:

#include <stdio.h>#include <windows.h>#include <conio.h>const char *pStrPipeName = "\\\\.\\pipe\\NamePipe_MoreWindows";int main()printf("        命名管道 客戶端\n");   printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");   printf("按任意鍵以開始連線命名管道\n"); getch(); printf("開始等待命名管道\n"); if (WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER) == FALSE) {  printf("Error! 連線命名管道失敗\n");  return 0; } printf("開啟命名管道\n"); HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ | GENERIC_WRITE, 0,  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); printf("向服務端傳送資料\n"); const int BUFFER_MAX_LEN = 256char szBuffer[BUFFER_MAX_LEN]; DWORD dwLen = 0//向服務端傳送資料 sprintf(szBuffer,"程序%d說\"%s\"", GetCurrentProcessId(), "Hello World!"); WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &dwLen, NULL); printf("資料寫入完畢共%d位元組\n", dwLen); //接收服務端發回的資料 ReadFile(hPipe, szBuffer, BUFFER_MAX_LEN, &dwLen, NULL);//讀取管道中的內容(管道是一種特殊的檔案) printf("接收服務端發來的確認資訊長度為%d位元組\n", dwLen); printf("具體資料內容如下:%s\n", szBuffer);  CloseHandle(hPipe); return 0;}

執行結果如下所示,執行時先啟動伺服器,然後再執行客戶端:

 

命名管道就先介紹到這裡了,管道技術上、中、下三篇到此也就全部結束下,下面給出目錄,方便大家檢視。

後面將有文章介紹程序通訊中最底層,最高效的方法——共享記憶體。歡迎繼續瀏覽。

1 其實程序執行緒同步除了使用中的介紹的關鍵段事件互斥量訊號量讀寫鎖。管道也可以用於執行緒的同步。

---------------------------------------------------華麗的分割線-----------------------------------------------------------------

 CSDN部落格之星評選活動正在進行,覺得本部落格對您有幫助,麻煩投我一票,非常感謝。您的支援就是我寫作的最大動力。

謝謝大家!