zoukankan      html  css  js  c++  java
  • 进程间通信二:管道技术之命名管道

    转载自:http://blog.csdn.net/morewindows/article/details/8260087

    本篇将讲解管道技术中的命名管道(Named Pipes),顾名思义,这个管道肯定是有名字的,联想到秒杀多线程面试题中的事件互斥量信号量(见附1,它们的名字主要是用于确保多个进程访问同一个对象。因此肯定也可以通过管道的名字来确保多个进程访问同一个管道。事实上,命名管道不仅可在同一台计算机的不同进程之间传输数据,甚至能在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。

     

    先来看看如何创建和使用命名管道。

    第一个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.服务端使用DisconnectNamedPipeCloseHandle关闭管道。

    代码中的CreateFile,WriteFile,ReadFile请参见上一篇《进程通信之二管道技术第二篇匿名管道》中的介绍。

     

    服务端代码如下:

    1. #include <stdio.h>  
    2. #include <windows.h>  
    3. #include <conio.h>  
    4. const char *pStrPipeName = "\\.\pipe\NamePipe_MoreWindows";  
    5. int main()  
    6. {  
    7.     printf("        命名管道 服务器 ");    
    8.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) -- ");    
    9.       
    10.     printf("创建命名管道并等待连接 ");  
    11.     HANDLE hPipe = CreateNamedPipe(pStrPipeName, PIPE_ACCESS_DUPLEX,   
    12.         PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,   
    13.         PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, 0);  
    14.     if (ConnectNamedPipe(hPipe, NULL) != NULL)//等待连接。  
    15.     {  
    16.         printf("连接成功,开始接收数据 ");  
    17.   
    18.         const int BUFFER_MAX_LEN = 256;  
    19.         char szBuffer[BUFFER_MAX_LEN];  
    20.         DWORD dwLen;  
    21.   
    22.         //接收客户端发送的数据  
    23.         ReadFile(hPipe, szBuffer, BUFFER_MAX_LEN, &dwLen, NULL);//读取管道中的内容(管道是一种特殊的文件)  
    24.         printf("接收到数据长度为%d字节 ", dwLen);  
    25.         printf("具体数据内容如下:%s ", szBuffer);  
    26.   
    27.         //确认已收到数据  
    28.         printf("向客户端发送已经收到标志 ");  
    29.         strcpy(szBuffer, "服务器已经收到");  
    30.         WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &dwLen, NULL);  
    31.     }  
    32.     DisconnectNamedPipe(hPipe);   
    33.     CloseHandle(hPipe);//关闭管道  
    34.     return 0;  
    35. }  

    客户端代码如下:

    1. #include <stdio.h>  
    2. #include <windows.h>  
    3. #include <conio.h>  
    4. const char *pStrPipeName = "\\.\pipe\NamePipe_MoreWindows";  
    5. int main()  
    6. {  
    7.     printf("        命名管道 客户端 ");    
    8.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) -- ");    
    9.   
    10.     printf("按任意键以开始连接命名管道 ");  
    11.     getch();  
    12.     printf("开始等待命名管道 ");  
    13.     if (WaitNamedPipe(pStrPipeName, NMPWAIT_WAIT_FOREVER) == FALSE)  
    14.     {  
    15.         printf("Error! 连接命名管道失败 ");  
    16.         return 0;  
    17.     }  
    18.   
    19.     printf("打开命名管道 ");  
    20.     HANDLE hPipe = CreateFile(pStrPipeName, GENERIC_READ | GENERIC_WRITE, 0,  
    21.         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  
    22.   
    23.     printf("向服务端发送数据 ");  
    24.     const int BUFFER_MAX_LEN = 256;  
    25.     char szBuffer[BUFFER_MAX_LEN];  
    26.     DWORD dwLen = 0;  
    27.   
    28.     //向服务端发送数据  
    29.     sprintf(szBuffer,"进程%d说"%s"", GetCurrentProcessId(), "Hello World!");  
    30.     WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &dwLen, NULL);  
    31.     printf("数据写入完毕共%d字节 ", dwLen);  
    32.   
    33.     //接收服务端发回的数据  
    34.     ReadFile(hPipe, szBuffer, BUFFER_MAX_LEN, &dwLen, NULL);//读取管道中的内容(管道是一种特殊的文件)  
    35.     printf("接收服务端发来的确认信息长度为%d字节 ", dwLen);  
    36.     printf("具体数据内容如下:%s ", szBuffer);  
    37.       
    38.     CloseHandle(hPipe);  
    39.     return 0;  
    40. }  

    运行结果如下所示,运行时先启动服务器,然后再运行客户端:

     

  • 相关阅读:
    建议使用nullptr而不是NULL
    二叉树的遍历(三种遍历方式,前中后序;递归,栈,迭代实现)
    455.分发饼干Easy Leetcode
    java 报错:Implicit super constructor Point() is undefined. Must explicitly invoke another constructor
    求解字符串所包含子串的个数
    数组去重
    vue watch
    mysql设置指定字段不能为负数
    Shell脚本监控服务存活状态及异常,并触发钉钉webhook报警
    Shell常用语法
  • 原文地址:https://www.cnblogs.com/wang-can/p/3331115.html
Copyright © 2011-2022 走看看