1. 程式人生 > >C/S模型之命名管道

C/S模型之命名管道

實例 命名 效果 bre turn tchar efault 介紹 容量

說明:利用管道實現服務端與客戶端之間的交互。效果等同於利用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模型之命名管道