一、前言
之前的 【學(xué)習(xí)筆記3】 中講述了匿名管道的實(shí)現(xiàn)方法,本文來學(xué)習(xí)一下命名管道的內(nèi)容,希望對自己與各位有所幫助。
二、基本概念
命名管道(Named Pipes),按照字面意思理解就是有名字的管道,它可在同一臺(tái)計(jì)算機(jī)的不同進(jìn)程之間或在跨越一個(gè)網(wǎng)絡(luò)的不同計(jì)算機(jī)的不同進(jìn)程之間,支持可靠的、單向或雙向的數(shù)據(jù)通信。
三、命名管道的創(chuàng)建和使用
3.1 函數(shù)原型
3.1.1 CreateNamedPipe
/* 創(chuàng)建命名管道 */
HANDLE WINAPI CreateNamedPipe(
/**
* 管道名稱。
* 形式:\\.\pipe\pipename。
* 最長256個(gè)字符,且不區(qū)分大小寫。
* 如果已有同名管道,則創(chuàng)建該管道的新實(shí)例。
*/
LPCTSTR lpName,
/**
* 管道打開方式。
* 常用的管道打開方式有以下三種,更多請查閱MSDN:
* PIPE_ACCESS_DUPLEX:該管道是雙向的,服務(wù)器和客戶端進(jìn)程都可以從管道讀取或者向管道寫入數(shù)據(jù)。
* PIPE_ACCESS_INBOUND:該管道中數(shù)據(jù)是從客戶端流向服務(wù)端,即客戶端只能寫,服務(wù)端只能讀。
* PIPE_ACCESS_OUTBOUND:該管道中數(shù)據(jù)是從服務(wù)端流向客戶端,即客戶端只能讀,服務(wù)端只能寫。
*/
DWORD dwOpenMode,
/**
* 管道模式。
* 常用的管道模式如下,更多請查閱MSDN:
* PIPE_TYPE_BYTE:數(shù)據(jù)作為一個(gè)連續(xù)的字節(jié)數(shù)據(jù)流寫入管道。
* PIPE_TYPE_MESSAGE:數(shù)據(jù)用數(shù)據(jù)塊(名為“消息”或“報(bào)文”)的形式寫入管道。
* PIPE_READMODE_BYTE:數(shù)據(jù)以單獨(dú)字節(jié)的形式從管道中讀出。
* PIPE_READMODE_MESSAGE:數(shù)據(jù)以名為“消息”的數(shù)據(jù)塊形式從管道中讀出(要求指定PIPE_TYPE_MESSAGE)。
* PIPE_WAIT:同步操作在等待的時(shí)候掛起線程。
* PIPE_NOWAIT:同步操作立即返回。
*/
DWORD dwPipeMode,
/**
* 該管道能創(chuàng)建的最大實(shí)例數(shù)。
* 必須大于1,小于PIPE_UNLIMITED_INSTANCES(255)。
*/
DWORD nMaxInstances,
DWORD nOutBufferSize, // 管道輸出緩沖區(qū)容量,設(shè)置0時(shí)使用默認(rèn)大小
DWORD nInBufferSize, // 管道輸入緩沖區(qū)容量,設(shè)置0時(shí)使用默認(rèn)大小
DWORD nDefaultTimeOut, // 管道默認(rèn)等待超時(shí)
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 管道的安全屬性
);
返回值:CreateNamedPipe() 執(zhí)行成功返回命名管道的句柄,否則返回INVALID_HANDLE_VALUE。
3.1.2 ConnectNamedPipe
/* 等待客戶端連接命名管道 */
BOOL WINAPI ConnectNamedPipe(
HANDLE hNamedPipe, // 命名管道的句柄
LPOVERLAPPED lpOverlapped // 指向 OVERLAPPED 結(jié)構(gòu)的指針,一般置為NULL即可
);
3.1.3 WaitNamedPipe
/* 客戶端連接命名管道 */
BOOL WINAPI WaitNamedPipe(
LPCTSTR lpNamedPipeName, // 管道名稱。形式:\\servername\pipe\pipename。本機(jī)管道的 servername 為"."。
/**
* 等待命名管道一個(gè)實(shí)例有效的超時(shí)時(shí)間(單位:毫秒)。
* NMPWAIT_USE_DEFAULT_WAIT:使用命名管道設(shè)定值(調(diào)用CreateNamedPip()時(shí)設(shè)置的 nDefaultTimeOut)。
* NMPWAIT_WAIT_FOREV:一直等待。
*/
DWORD nTimeOut
);
返回值:WaitNamedPipe() 在指定時(shí)間內(nèi)連接成功返回TRUE,否則返回FALSE。
注意:
- 在指定時(shí)間內(nèi)連接成功返回TRUE,否則返回FALSE。
- 如果函數(shù)執(zhí)行成功返回TRUE,表示至少有一個(gè)命名管道的實(shí)例有效,接下來應(yīng)該使用 CreateFile() 函數(shù)打開命名管道的一個(gè)句柄,但是 CreateFile() 可能會(huì)打開管道失敗,因?yàn)樵搶?shí)例有可能被服務(wù)端關(guān)閉或被已經(jīng)被其他客戶端打開。
3.2 示例代碼
3.2.1 實(shí)現(xiàn)步驟
- 服務(wù)端調(diào)用 CreateNamedPipe() 創(chuàng)建命名管道并調(diào)用 ConnectNamedPipe() 等待客戶端連接。
- 客戶端使調(diào)用 WaitNamedPipe() 連接成功后,再調(diào)用 CreateFile() 和 WriteFile() 打開管道并向管道中寫入一段數(shù)據(jù),即向服務(wù)端發(fā)送消息。
- 服務(wù)端調(diào)用 ReadFile() 從管道中讀取數(shù)據(jù)后(即收到消息),再向管道中寫入確認(rèn)信息表明已經(jīng)收到數(shù)據(jù),即通知客戶端已收到。
- 客戶端收到確認(rèn)信息后結(jié)束,調(diào)用 CloseHandle() 關(guān)閉管道(該管道是 CreateFile() 打開的)。
- 服務(wù)端使用 DisconnectNamedPipe() 和 CloseHandle() 斷開連接并關(guān)閉管道。
3.2.2 服務(wù)端代碼
#include <stdio.h>
#include <windows.h>
#include <conio.h>
int main()
{
const char *pStrPipeName = "\\\\.\\pipe\\MyNamePipe";
// 創(chuàng)建管道
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 (hPipe != INVALID_HANDLE_VALUE) {
printf("Successfully created pipe!\n");
printf("Waiting for client to connect...\n");
}
// 等待客戶端連接
if (ConnectNamedPipe(hPipe, NULL) != NULL) {
printf("The connection is successful!\n");
printf("Start to receive data:\n");
CHAR Buffer[1024] = { 0 };
DWORD dwNumberOfBytesRead = 0;
// 接收客戶端發(fā)送的數(shù)據(jù)
ReadFile(hPipe, Buffer, sizeof(Buffer), &dwNumberOfBytesRead, NULL);
printf("data len: %d\n", dwNumberOfBytesRead);
printf("content:%s\n", Buffer);
// 確認(rèn)已收到數(shù)據(jù)
printf("Send a received flag to the client!\n");
strcpy(Buffer, "done");
WriteFile(hPipe, Buffer, strlen(Buffer) + 1, &dwNumberOfBytesRead, NULL);
}
// 斷開連接并關(guān)閉管道
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
return 0;
}
3.2.3 客戶端代碼
#include <stdio.h>
#include <windows.h>
#include <conio.h>
int main()
{
const char *pStrPipeName = "\\\\.\\pipe\\MyNamePipe";
printf("Connecting to pipe...\n");
if (WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER) == FALSE) {
printf("Failed to connect to pipe!\n");
return 0;
}
printf("Open pipe!\n");
HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
printf("Send data to the server!\n");
CHAR Buffer[1024] = { 0 };
DWORD dwNumberOfBytesWritten = 0;
// 向服務(wù)端發(fā)送數(shù)據(jù)
sprintf(Buffer, "Process %d said: %s", GetCurrentProcessId(), "Hello World!");
WriteFile(hPipe, Buffer, strlen(Buffer) + 1, &dwNumberOfBytesWritten, NULL);
printf("Send data len :%d \n", dwNumberOfBytesWritten);
// 接收服務(wù)端發(fā)回的數(shù)據(jù)
ReadFile(hPipe, Buffer, sizeof(Buffer), &dwNumberOfBytesWritten, NULL);
printf("Reply data len: %d\n", dwNumberOfBytesWritten);
printf("content:%s\n", Buffer);
CloseHandle(hPipe);
return 0;
}
3.2.4 輸出結(jié)果
運(yùn)行結(jié)果如下所示,運(yùn)行時(shí)先啟動(dòng)服務(wù)器,然后再啟動(dòng)客戶端:

在這里插入圖片描述
四、總結(jié)
由此可見,命名管道的用法其實(shí)與套接字非常類似,也是一種實(shí)現(xiàn)進(jìn)程間通信的方法。
至此,管道通信的學(xué)習(xí)完畢,以下時(shí)管道通信的相關(guān)筆記,方便查閱: