【學(xué)習(xí)筆記5】管道通信:命名管道

一、前言

之前的 【學(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。

注意:

  1. 在指定時(shí)間內(nèi)連接成功返回TRUE,否則返回FALSE。
  2. 如果函數(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)步驟

  1. 服務(wù)端調(diào)用 CreateNamedPipe() 創(chuàng)建命名管道并調(diào)用 ConnectNamedPipe() 等待客戶端連接。
  2. 客戶端使調(diào)用 WaitNamedPipe() 連接成功后,再調(diào)用 CreateFile() 和 WriteFile() 打開管道并向管道中寫入一段數(shù)據(jù),即向服務(wù)端發(fā)送消息。
  3. 服務(wù)端調(diào)用 ReadFile() 從管道中讀取數(shù)據(jù)后(即收到消息),再向管道中寫入確認(rèn)信息表明已經(jīng)收到數(shù)據(jù),即通知客戶端已收到。
  4. 客戶端收到確認(rèn)信息后結(jié)束,調(diào)用 CloseHandle() 關(guān)閉管道(該管道是 CreateFile() 打開的)。
  5. 服務(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)筆記,方便查閱:

【學(xué)習(xí)筆記2】管道通信:輸入輸出重定向

【學(xué)習(xí)筆記3】管道通信:匿名管道

【學(xué)習(xí)筆記5】管道通信:命名管道

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容