管道通信 C++

Posted

技术标签:

【中文标题】管道通信 C++【英文标题】:Pipe communication C++ 【发布时间】:2009-12-04 21:57:42 【问题描述】:

我正在编写两个必须通信的小 C++ 应用程序。第一个将是一项服务,每隔一段时间,它必须提醒用户一些事情。由于服务无法创建窗口,因此我将应用设计为两个独立的可执行文件。

服务将使用通知器进行通信。

该服务只需要向通知器发送文本消息,它将在系统托盘上显示一个气球。

我正在尝试使用命名管道,我想我快到了,但还不完全到那里。到目前为止我所拥有的是:

在通知方:

  m_hInPipe = CreateNamedPipe(L"\\\\.\\pipe\\nhsupspipe", PIPE_ACCESS_INBOUND,
                PIPE_WAIT, 1, 1024, 1024, 60, NULL);

意思是我创建了一个名为 nhsupspipe 的管道,一个入站管道。

在服务端:

if (!WriteFile(m_hOutPipe, "My message to the user?", 23, &escritos, &o))
     std::cout << "ERROR: " << GetLastError();

调试我可以看到一切正常,管道已创建,WriteFile 将我的 23 个字节写入管道。

我的问题是:如何在通知方读取这些字节?是否有任何消息发送到进程?我必须为管道编写处理程序吗?有什么事吗?

【问题讨论】:

【参考方案1】:

来自客户端(您的服务)和服务器(通知程序)的一些简单的 sn-ps [注意:这是改编自我不久前完成的一个项目,而该项目又受到来自 CreateNamedPipe & co 的 MSDN 示例的严重“影响”]:

服务器端:

HANDLE hPipe = INVALID_HANDLE_VALUE;
bool bConnected = false;

 hPipe = CreateNamedPipe( L"\\\\.\\pipe\\nhsupspipe",
                          PIPE_ACCESS_DUPLEX,
                          PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                          PIPE_UNLIMITED_INSTANCES,
                          sizeof( Message ),
                          0,
                          0,
                          NULL );

 // failed to create pipe?
 if( hPipe == INVALID_HANDLE_VALUE )
   return -1;
 

 // Wait for the client to connect; if it succeeds, 
 // the function returns a nonzero value. If the function
 // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 
 bConnected = ConnectNamedPipe( hPipe, NULL ) ? true : ( GetLastError() == ERROR_PIPE_CONNECTED );

 if( bConnected )
  while( true ) 
   unsigned long ulBytesRead = 0;
   // read client requests from the pipe. 
   bool bReadOk = ReadFile( hPipe,
                            &message,
                            sizeof( message ),
                            &ulBytesRead,
                            NULL );

   // bail if read failed [error or client closed connection]
   if( !bReadOk || ulBytesRead == 0 )
    break;

   // all ok, process the message received

  
 
 else
   // the client could not connect, so close the pipe. 
   CloseHandle( hPipe );
 

 return 0;

客户:

HANDLE hPipe = INVALID_HANDLE_VALUE;

 // create the named pipe handle
 hPipe = CreateFile( L"\\\\.\\pipe\\nhsupspipe",
                     GENERIC_READ | GENERIC_WRITE,
                     0, 
                     NULL, 
                     OPEN_EXISTING,
                     0,
                     NULL );

 // if everything ok set mode to message mode
 if( INVALID_HANDLE_VALUE != hPipe )
  DWORD dwMode = PIPE_READMODE_MESSAGE;
  // if this fails bail out
  if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) )
   CloseHandle( hPipe ); 

   return -1;
  
 

 unsigned long ulBytesWritten = 0;
 bool bWriteOk = WriteFile( hPipe, 
                            ( LPCVOID )&message, 
                            sizeof( Message ), 
                            &ulBytesWritten, 
                            NULL );

 // check if the writing was ok
 if( !bWriteOk || ulBytesWritten < sizeof( Message ) )
  return -1;
 

 // written ok

 return 0;

上面提到的Message是一个结构,它将成为您的消息,您可能想要pack。

由于在您的场景中,客户端(服务)可能会在服务器(通知程序)之前启动并运行,因此您需要在客户端制定某种重新连接策略。

另外,你应该仔细考虑奥斯特曼先生在他的reply 中所说的话(即使没有别的,因为他是拉里奥斯特曼)。

【讨论】:

你能告诉我如何在 VS (C++) 的单个项目中实现服务器和客户端吗? @k_programmer 如果你希望两者都在同一个进程中,你要么必须为服务器和客户端使用单独的线程,要么使用 ReadFileEx 和 WriteFileEx。尝试实施解决方案,如果您有问题,请在此处提问。 感谢您的回复。我想出了一种在同一个线程中实现服务器和客户端的方法。看起来很奇怪,但我以某种方式做到了,现在它工作正常:) @k_programmer 对于半双工协议,您不需要单独的线程。不管怎样,很高兴知道你已经弄明白了。【参考方案2】:

通常在线程或消息循环中,您需要在通知程序端使用 ReadFile 或 ReadFileEx(用于重叠 I/O)。另请查看CreateNamedPipe 和WaitNamedPipe 的文档。

【讨论】:

【参考方案3】:

在这种情况下,我可能会使用 RPC 而不是原始命名管道。使用原始命名管道,您必须解析来自客户端的数据,这会引入安全漏洞的可能性。使用 RPC,您可以让 RPC 为您解析数据,从而减少引入错误的机会。

【讨论】:

【参考方案4】:

不完全是问题,但另一个选项是 CreateEvent() 和内存映射文件。

【讨论】:

【参考方案5】:

如果我这样做,我会让服务端执行CreateNamedPipe(出站),然后调用ConnectNamedPipe 等待通知器连接。

在通知者方面,我会使用 CreateFileFILE_FLAG_OVERLAPPED。这样,当数据可供读取时,将发出管道句柄的信号。您的通知程序(可能)也将维护一个 GUI,因此您可能希望它在其事件循环中调用 MsgWaitForMultipleObjects。这将等待消息被处理进入管道的数据被处理。这几乎就像一个正常的事件循环一样工作,除了它的返回值与GetMessage() 略有不同——如果你的句柄被发出信号,它将返回WAIT_OBJECT_0,或者如果你有一条消息,它将返回WAIT_OBJECT_0 + 1(假设你只让它在一个句柄上等待——它实际上是WAIT_OBJECT_0 + N,其中N 是你让它等待的句柄数)。在大多数情况下,它就像一个普通的 PeekMessageGetMessage 循环——等到有事要做,做,再等。

【讨论】:

以上是关于管道通信 C++的主要内容,如果未能解决你的问题,请参考以下文章

管道通信 C++

C++ 命名管道 与Winform跨进程通信

使用命名管道的两种 C++ 到 C# 通信

c#命名管道双向通信

C++笔记--Linux编程(11)-进程通信

在 C++ 中创建多个进程并与管道通信