【问题标题】:Pipe communication C++管道通信 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 个字节写入管道。

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

【问题讨论】:

    标签: c++ windows pipe


    【解决方案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】:

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

    【讨论】:

      【解决方案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 循环——等到有事要做,做,再等。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-26
            • 2015-07-15
            相关资源
            最近更新 更多