C/S模型之命名管道
說明:利用管道實現服務端與客戶端之間的交互。效果等同於利用socket。
命名管道(NamedPipe)是一種簡單的進程間通信(IPC)機制,是服務器進程和一個或多個客戶進程之間通信的單向或雙向管道。
其本質是文件讀寫、內存共享。
采用命名管道完成進程通信的過程為:
1.在服務器端調用CreateNamedPipe創建命名管道之後,調用ConnectNamedPipe函數讓服務器進程等待客戶端進程連接到該命名管道的實例上。
2.在客戶端,首先調用WaitNamedPipe函數判斷當前是否有可以利用的命名管道實例,如果有就調用CreateFile函數打開該命名管道的實例,並建立一個連接。
之後就可以通過ReadFile、WriteFile進行通信。
函數講解:
第一個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_INSTANCES 255
第五個參數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關閉管道。
源代碼: //服務端 #include "stdafx.h" #include <Windows.h> int _tmain(int argc, _TCHAR* argv[]) { const char *szPipeName = "\\\\.\\pipe\\Pipe"; //創建命名管道 HANDLE hPipe = CreateNamedPipe( szPipeName, // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // max. instances MAXBYTE, // output buffer size MAXBYTE, // input buffer size 0, // client time-out NULL); // default security attribute if (hPipe == INVALID_HANDLE_VALUE) { printf("CreateNamedPipe Error :%d", GetLastError()); } char pRecvBuf[MAXBYTE] = { 0 }; DWORD dwLen=0; //連接管道 if (ConnectNamedPipe(hPipe, NULL)) { printf("連接成功,開始接收數據!\n"); while (true) { //取得客戶端的數據 if (ReadFile(hPipe, pRecvBuf, MAXBYTE, &dwLen, 0)) { printf("%s\n", pRecvBuf); } if (strcmp(pRecvBuf, "exit") == 0) { break; } //將收到的數據進行返回 if (WriteFile(hPipe, pRecvBuf, strlen(pRecvBuf) + sizeof(char), &dwLen, 0)) { } else { break; } } } // 關閉管道 DisconnectNamedPipe(hPipe); CloseHandle(hPipe); return 0; }
//客戶端 #include "stdafx.h" #include <Windows.h> int _tmain(int argc, _TCHAR* argv[]) { const char *szPipeName = "\\\\.\\pipe\\Pipe"; //檢測是否存在該命名管道 if (WaitNamedPipe(szPipeName, NMPWAIT_WAIT_FOREVER) == TRUE) { printf("連接命名管道成功!\n"); } //打開文件--連接管道 HANDLE hFile = CreateFile(szPipeName, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); char pSendBuf[MAXBYTE] = { 0 }; char pRecvBuf[MAXBYTE] = {0}; DWORD dwLen = 0; while (true) { gets_s(pSendBuf); //將管道傳數據 if(!WriteFile(hFile, pSendBuf, strlen(pSendBuf) + sizeof(char), &dwLen, NULL)) { printf("False\n"); } if (strcmp(pSendBuf, "exit") == 0) { break; } //讀取管道中的數據 if (ReadFile(hFile, pRecvBuf, MAXBYTE, &dwLen, 0)) { printf("return :%s \n", pRecvBuf); } } //關閉管道 CloseHandle(hFile); return 0; }
C/S模型之命名管道