• 进程的通信 - 命名管道


    命名管道概述

    命名管道(Named Pipes),顾名思义,一个有名字的管道。命名管道的名字主要是用于确保多个进程访问同一个对象。命名管道不仅可以在同一台计算机之间传输数据,甚至能在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信

    命名管道常用的API

    创建命名管道实例—CreateNamedPipe

    函数原型

    1. HANDLE CreateNamedPipeW(
    2. [in] LPCWSTR lpName,
    3. [in] DWORD dwOpenMode,
    4. [in] DWORD dwPipeMode,
    5. [in] DWORD nMaxInstances,
    6. [in] DWORD nOutBufferSize,
    7. [in] DWORD nInBufferSize,
    8. [in] DWORD nDefaultTimeOut,
    9. [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
    10. );
    • 参数lpName

    唯一的管道名称。 必须是"\\.\pipe\管道名称“的格式。最多为256个字符长度,且不区分大小。如果已经有相同的命名管道,则会创建那个管道的一个实例

    • 参数dwOpenMode

    表示管道的打开方式,同一管道的每个实例必须指定相同的打开方式

    模式意义

    PIPE_ACCESS_DUPLEX

    0x00000003

    管道是双向的;服务器和客户端进程都可以读取和写入管道。

    PIPE_ACCESS_INBOUND

    0x00000001

    管道中的数据流仅从客户端传输到服务器。

    PIPE_ACCESS_OUTBOUND

    0x00000002

    管道中的数据流仅从服务器传输到客户端。

    同时可以包含以下一个或多个标记,对于同一管道的不同实例,这些模式可以不同

    模式意义

    FILE_FLAG_FIRST_PIPE_INSTANCE

    0x00080000

    如果尝试使用此标志创建管道的多个实例,则第一个实例的创建成功,但下一个实例的创建失败,并ERROR_ACCESS_DENIED

    FILE_FLAG_WRITE_THROUGH

    0x80000000

    直写模式已启用。

    FILE_FLAG_OVERLAPPED

    0x40000000

    重叠模式已启用。

    还可以包含以下安全访问模式的组合,对于同一管道的不同实例,这些模式可以是不同。

    模式意义

    WRITE_DAC

    0x00040000L

    调用方将具有对命名管道的自由访问控制列表 (ACL) 的写入访问权限。

    WRITE_OWNER

    0x00080000L

    调用方将具有对命名管道所有者的写入访问权限。

    ACCESS_SYSTEM_SECURITY

    0x01000000L

    调用方将具有对命名管道的 SACL 的写入访问权限。有关详细信息,请参阅访问控制列表 (ACL) 和SACL 访问权限
    • 参数dwPipeMode

    管道模式,可以指定以下类型模式之一。必须为管道的每个实例指定为相同的类型模式 

    模式意义

    PIPE_TYPE_BYTE

    0x00000000

    数据以字节流的形式写入管道。

    PIPE_TYPE_MESSAGE

    0x00000004

    数据作为消息流写入管道。

    也可以指定以下读取模式之一。同一管道的不同实例可以指定不同的读取模式。

    模式意义

    PIPE_READMODE_BYTE

    0x00000000

    数据以字节流的形式从管道读取。此模式可用于PIPE_TYPE_MESSAGEPIPE_TYPE_BYTE

    PIPE_READMODE_MESSAGE

    0x00000002

    数据作为消息流从管道读取。仅当还指定了PIPE_TYPE_MESSAGE时,才能使用此模式。

     还可以指定以下等待模式之一。同一管道的不同实例可以指定不同的等待模式。

    PIPE_WAIT

    0x00000000

    阻止模式已启用。

    PIPE_NOWAIT

    0x00000001

    非阻塞模式已启用。在此模式下,ReadFileWriteFileConnectNamedPipe始终立即返回。

    还可以指定以下远程客户端模式之一。同一管道的不同实例可以指定不同的远程客户端模式。

    模式意义

    PIPE_ACCEPT_REMOTE_CLIENTS

    0x00000000

    可以接受来自远程客户端的连接,并根据管道的安全描述符进行检查。

    PIPE_REJECT_REMOTE_CLIENTS

    0x00000008

    来自远程客户端的连接将自动被拒绝。
    • 参数nMaxInstances

    指定管道可以创建的最大实例数,必须是1到常数255之间的一个值。管道的第一个实例可以指定这个值,管道的其他实例的这个值必须和第一个实例指定的这个值相同。

    • 参数nOutBufferSize

    表示管道的输出缓冲区的容量,0表示使用默认大小。

    • 参数nInBufferSize

    表示管道的输入缓冲区的容量,0表示使用默认大小

    • 参数nDefaultTimeOut

    表示管道的默认等待超时(ms单位),零表示默认超时为50毫秒

    • 参数lpSecurityAttributes

    指向SECURITY_ATTRIBUTES结构的指针,该结构体指定命名管道的安全描述符,并确定子进程是否继承返回的句柄。NULL表示使用默认安全描述符,并且无法继承句柄

    函数返回值
    函数执行成功返回命名管道的句柄,否则返回INVALID_HANDLE_VALUE

    等待客户端连接—ConnectNamdePipe

    函数原型

    1. BOOL ConnectNamedPipe(
    2. [in] HANDLE hNamedPipe,
    3. [in, out, optional] LPOVERLAPPED lpOverlapped
    4. );
    • 参数hNamedPipe

    表示管道的实例的服务端句柄。也就是CreateNamedPipe函数返回的句柄

    • 参数lpOverlapped

    如果CreateNamedPipe第一个参数指定了FILE_FLAG_OVERLAPPED,则此参数不能为NULL,参数就必须是一个手动重置事件对象的句柄。 

    例如下面这样

    1. //创建命名管道
    2. hNamedPipe = CreateNamedPipe(.., PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,..);
    3. //手动重置事件对象的句柄
    4. HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    5. //创建一个OVERLAPPED结构体
    6. OVERLAPPED ovlap;
    7. ZeroMemory(&ovlap, sizeof(OVERLAPPED));//底层调用的memset
    8. ovlap.hEvent = hEvent;
    9. //等待客户端连接
    10. ConnectNamedPipe(hNamedPipe,&ovlap);

     客户端连接命名管道—WaitNamedpipe

    1. BOOL WaitNamedPipeW(
    2. [in] LPCWSTR lpNamedPipeName,
    3. [in] DWORD nTimeOut
    4. );
    • 参数lpNamedPipeName

    表示命名管道的名称

    • 参数nTimeOut

    表示等待命名管道的实例可用的毫秒数。可以使用以下值之一,而不是指定毫秒数。

    取值意义

    NMPWAIT_USE_DEFAULT_WAIT

    0x00000000

    超时间隔是服务器进程在CreateNamedPipe函数中指定的默认值。

    NMPWAIT_WAIT_FOREVER

    0xffffffff

    在命名管道的实例可用之前,该函数不会返回。

    函数返回值

    如果管道的实例在超时之前可用,则返回值非零。否则返回零 。

    如果指定的命名管道不存在实例,则无论超时值设定如何,WaitNamedPipe函数会立即返回。

    如果函数执行成功,则进程可以使用CreateFile函数打开命名管道句柄。返回TRUE表示至少有一个管道实例可用。但也可能打开失败,例如当服务器关闭或者有其他客户端打开了管道。

    打开文件或I/O设备—CreateFile

    1. HANDLE CreateFileA(
    2. [in] LPCSTR lpFileName,
    3. [in] DWORD dwDesiredAccess,
    4. [in] DWORD dwShareMode,
    5. [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    6. [in] DWORD dwCreationDisposition,
    7. [in] DWORD dwFlagsAndAttributes,
    8. [in, optional] HANDLE hTemplateFile
    9. );
    • 参数lpFileName

    表示创建或打开的文件或设备名称

    • 参数dwDesiredAccess

    请求文件或设备的访问权限,有读、写、读写或无任何操作。GENERIC_READGENERIC_WRITE或两者(GENERIC_READ | GENERIC_WRITE

    • 参数dwShareMode

    文件或设备的请求共享模式,有读、写、读写、删除、读写删或者无任何模式

    价值意义

    0

    防止其他进程在请求删除、读取或写入访问权限时打开文件或设备。

    FILE_SHARE_DELETE

    对文件或设备启用后续打开操作以请求删除访问权限。

    FILE_SHARE_READ

    启用对文件或设备的后续打开操作以请求读取访问权限。

    FILE_SHARE_WRITE

    允许对文件或设备执行后续打开操作以请求写入访问权限。
    • 参数 lpSecurityAttributes

    指向SECUTITY_ATTRIBUTES结构的指针,参数为NULL表示任何子进程都不能继承CreateFile返回的句柄

    • 参数dwCreationDisposition

    表示对存在会不存在的文件或设备执行的操作,对存在的文件或设备通常设置为OPEN_EXISTING

    取值意义

    CREATE_ALWAYS

    始终创建新文件。

    CREATE_NEW

    仅当文件尚不存在时才创建新文件。

    OPEN_ALWAYS

    始终打开文件。

    OPEN_EXISTING

    仅打开文件或设备(如果存在)。

    TRUNCATE_EXISTING

    打开文件并将其截断,使其大小为零字节(仅当它存在时)。
    • 参数dwFlagsAndAttributes

    表示文件或设备属性和标志,FILE_ATTRIBUTE_NORMAL是文件的通用默认属性 

    • 参数hTemplateFile

    此参数可为NULL,打开现有文件时,创建文件时可用忽略这个参数

    函数返回值

    函数执行成功,返回值是指定文件、设备、命名管道或邮槽

    函数失败返回INVALID_HANDLE_VALUE

    Demo示例:

     

    两个MFC应用,给第一个应用添加三个菜单分别为”创建管道“,”读取数据“,”写入数据“作为服务端。点击”创建管道“服务端会创建一个管道,然后等待连接;点击“读取数据”服务端会读取管道中的数据,然后通过消息提示框显示出来; 点击“写入数据”服务端会向命名管道写入数据。

    第二个应用做客户端,为其添加三个菜单分别为“连接管道”、“读取数据”、“写入数据”。点击“连接管道”客户端会去连接命名管道;点击“读取数据”客户端会读取管道中的数据,然后通过消息提示框显示出来;点击“写入数据”客户端会向管道中写入数据。

    服务端

    创建管道:

    服务端调ConnectNamedPipe等待客户端连接,这里我将参数lpOverlapped设定为一个手动重置的事件对象,当成功连接后,系统会将这个事件对象设为已通知状态,我们可以监听这个对象来判断客户端是否成功连接。

    hNamedPipe是一个HANDLE类型的类属性,在类的构造函数里初始化,析构函数里销毁。

      

      

    1. void CChildView::OnCreatNamePipe()
    2. {
    3. //1.创建一个命名管道
    4. LPCTSTR szPipeName = TEXT("\\\\.\\pipe\\mypipe");
    5. hNamedPipe = CreateNamedPipe(szPipeName,
    6. PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
    7. PIPE_TYPE_BYTE,
    8. 1, 1024, 1024, 0, NULL
    9. );
    10. if (hNamedPipe == INVALID_HANDLE_VALUE) {
    11. TRACE("Create NamedPipe failed witch %d\n", GetLastError());
    12. MessageBox(_T("创建命名管道失败"));
    13. return;
    14. }
    15. //连接完成后,系统会将OVERLAPPED的hEvent设置为已通知状态事件
    16. //这里创建一个事件赋值给hEvent,用来监控其改变
    17. HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    18. if (hEvent == NULL) {
    19. MessageBox(_T("创建事件失败"));
    20. CloseHandle(hNamedPipe);
    21. hNamedPipe = NULL;
    22. return;
    23. }
    24. //2.等待客户端的连接
    25. OVERLAPPED ovlap;
    26. ZeroMemory(&ovlap, sizeof(OVERLAPPED));//底层调用的memset
    27. ovlap.hEvent = hEvent;
    28. if (!ConnectNamedPipe(hNamedPipe,&ovlap)) {
    29. //标准判断操作
    30. if (ERROR_IO_PENDING != GetLastError()) {
    31. MessageBox(_T("等待客户端连接失败"));
    32. CloseHandle(hNamedPipe);
    33. hNamedPipe = NULL;
    34. return;
    35. }
    36. }
    37. if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED) {
    38. //
    39. MessageBox(_T("等待对象失败"));
    40. CloseHandle(hNamedPipe);
    41. CloseHandle(hEvent);
    42. hNamedPipe = NULL;
    43. hEvent = NULL;
    44. return;
    45. }
    46. //否则就连接成功
    47. }

    读数据:

    1. void CChildView::OnSreadNamePipe()
    2. {
    3. char szBuf[100] = { 0 };
    4. DWORD dwRead;
    5. if (!ReadFile(hNamedPipe, szBuf, 100, &dwRead, NULL)) {
    6. MessageBox(_T("读取数据失败"));
    7. return;
    8. }
    9. MessageBox((CString)szBuf);
    10. }

    写数据:

    1. void CChildView::OnSwriteNamePipe()
    2. {
    3. char szBuf[] = "霸道小明超秀";
    4. DWORD dwWrite;
    5. if (!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL)) {
    6. MessageBox(_T("写入数据失败"));
    7. return;
    8. }
    9. }

    客户端 

     连接管道:

    hNamedPipe是一个HANDLE类型是类属性,在构造函数里初始化,在析构函数里销毁。

       

      

    1. void CChildView::OnConnectNamePipe()
    2. {
    3. //连接命名管道
    4. LPCTSTR szNamedPipe = TEXT("\\\\.\\pipe\\mypipe");
    5. if (0 == WaitNamedPipe(szNamedPipe, NMPWAIT_WAIT_FOREVER)) {
    6. MessageBox(_T("当前没有可以利用的管道"));
    7. return;
    8. }
    9. hNamedPipe = CreateFile(szNamedPipe,
    10. GENERIC_READ | GENERIC_WRITE,
    11. 0,
    12. NULL,
    13. OPEN_EXISTING,
    14. FILE_ATTRIBUTE_NORMAL,
    15. NULL);
    16. if (hNamedPipe == INVALID_HANDLE_VALUE) {
    17. TRACE("Create File failed with %d\n", GetLastError());
    18. MessageBox(_T("打开命名管道失败!"));
    19. hNamedPipe = NULL;
    20. return;
    21. }
    22. //连接成功
    23. }

    读数据:

    1. void CChildView::OnReadNamePipe()
    2. {
    3. char szBuf[100] = { 0 };
    4. DWORD dwRead;
    5. if (!ReadFile(hNamedPipe, szBuf, 100, &dwRead, NULL)) {
    6. MessageBox(_T("读取数据失败"));
    7. return;
    8. }
    9. MessageBox((CStringW)szBuf);
    10. }

    写数据:

    1. void CChildView::OnWriteNamePipe()
    2. {
    3. char szBuf[] = "霸道小明超秀";
    4. DWORD dwWrite;
    5. if(!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL)) {
    6. MessageBox(_T("写入数据失败"));
    7. return;
    8. }
    9. }

    执行结果:

     

  • 相关阅读:
    网络编程TP/IP (尹圣雨)(韩) 第二章 课后习题
    PDF控件Spire.PDF for .NET【安全】演示:向 PDF 文件添加到期日期
    (附源码)app学生社团管理系统 毕业设计 191850
    操作系统 应用题 例题+参考答案(考研真题)
    ImportError: DLL load failed while importing MPI: 找不到指定的模块
    【自然语言处理(NLP)】基于SQuAD的机器阅读理解
    强大博客搭建全过程(1)-hexo博客搭建保姆级教程
    アィシャ / 艾夏
    ExtJS - UI组件 - Chart
    《构建之法》笔记---第十章 典型用户和场景
  • 原文地址:https://blog.csdn.net/qq_54169998/article/details/127953188