• 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
    表示管道名称,采用的形式是:\.pipepipename。最多可达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
    表示管道名称,采用的形式是:\servernamepipepipename。如果是本机管道,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("连接成功,开始接收数据!
    ");
            while (true)
            {
                //取得客户端的数据
                if (ReadFile(hPipe, pRecvBuf, MAXBYTE, &dwLen, 0))
                {
                    printf("%s
    ", 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("连接命名管道成功!
    ");
        }
    
        //打开文件--连接管道
        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
    ");
                }
                if (strcmp(pSendBuf, "exit") == 0)
                {
                    break;
                }
                //读取管道中的数据
                if (ReadFile(hFile, pRecvBuf, MAXBYTE, &dwLen, 0))
                {
                    printf("return :%s 
    ", pRecvBuf);
                }
                
            }
        
        //关闭管道
        CloseHandle(hFile);
        return 0;
    }
  • 相关阅读:
    C# 接口显示实现和隐式实现
    接口压力测试控制台程序
    超全面常用的数据库优化方案
    使用POI导出Excel时,关于设置带有多行表头表格自动宽度的问题解决办法
    win7 64位多出莫名用户,如何去掉?
    MYSQL:frm,myd,myi文件恢复至数据库的方法
    SpringBoot集成Dubbo
    Spring事务传播特性
    搭建Spring源码环境
    JVM常用命令
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/7694180.html
Copyright © 2020-2023  润新知