程序通訊之二 管道技術第三篇 命名管道
上一篇《程序通訊之二管道技術第二篇匿名管道》中講解了匿名管道,匿名管道有讀取端和寫入端,在建立匿名管道(CreatePipe)後就可以像讀寫檔案一樣的對管道中進行讀寫(ReadFile與WriteFile,注意讀寫順序)。在關閉匿名管道兩端後會由系統負責銷燬並回收資源。文章中還示範了父程序如何使用匿名管道來改變子程序的輸入輸出。
本篇將講解管道技術中的命名管道(Named Pipes),顧名思義,這個管道肯定是有名字的,聯想到中的事件、互斥量、訊號量(見附1),它們的名字主要是用於確保多個程序訪問同一個物件。因此肯定也可以通過管道的名字來確保多個程序訪問同一個管道。事實上,命名管道不僅可在同一臺計算機的不同程序之間傳輸資料,甚至能在跨越一個網路的不同計算機的不同程序之間,支援可靠的、單向或雙向的資料通訊。
先來看看如何建立和使用命名管道。
第一個CreateNamedPipe
函式功能:建立命名管道
函式原型:
HANDLEWINAPICreateNamedPipe(
LPCTSTRlpName,
DWORDdwOpenMode,
DWORDdwPipeMode,
DWORDnMaxInstances,
DWORDnOutBufferSize,
DWORDnInBufferSize,
DWORDnDefaultTimeOut,
LPSECURITY_ATTRIBUTESlpSecurityAttributes
);
引數說明:
第一個引數LPCTSTRlpName
表示管道名稱,採用的形式是:\\.\pipe\pipename
第二個引數DWORDdwOpenMode
表示管道的開啟方式。下面列出最常用的三種,更多請參閱MSDN。
1.PIPE_ACCESS_DUPLEX
該管道是雙向的,伺服器和客戶端程序都可以從管道讀取或者向管道寫入資料。
2.PIPE_ACCESS_INBOUND
該管道中資料是從客戶端流向服務端,即客戶端只能寫,服務端只能讀。
3.PIPE_ACCESS_OUTBOUND
該管道中資料是從服務端流向客戶端,即客戶端只能讀,服務端只能寫。
第三個引數DWORDdwPipeMode
表示管道的模式,下面是一些常用模式介紹,更多請參閱
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.服務端使用DisconnectNamedPipe和CloseHandle關閉管道。
程式碼中的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 = 256; char 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部落格之星評選活動正在進行,覺得本部落格對您有幫助,麻煩投我一票,非常感謝。您的支援就是我寫作的最大動力。
謝謝大家!