【问题标题】:how to make client to check if the server is done with read operation from pipe before writing data into pipe in c++c++ - 在将数据写入管道之前,如何让客户端检查服务器是否完成了管道的读取操作
【发布时间】:2018-07-02 09:00:48
【问题描述】:

我需要我的客户端首先检查服务器是否正在从管道读取数据,如果是,则等待服务器完成,否则将数据写入管道。如果我不在示例客户端程序中使用 sleep 命令,则服务器无法正确读取消息。我从它说的文档中找到了这个问题的原因:

此缓冲区必须在读取操作期间保持有效。在读取操作完成之前,调用者不得使用此缓冲区。

但它没有指定在读取操作完成之前如何阻止客户端。

服务器代码:

#include<stdio.h>
#include<windows.h>
#include<iostream>
using namespace std;
int main(void)
{
    HANDLE hPipe;
    char buffer[1024];
    DWORD dwRead;


hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),
                        PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
                        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                        1,
                        1024 * 16,
                        1024 * 16,
                        NMPWAIT_USE_DEFAULT_WAIT,
                        NULL);
while (hPipe != INVALID_HANDLE_VALUE)
{
    if (ConnectNamedPipe(hPipe, NULL) != FALSE)   // wait for someone to connect to the pipe
    {
        while (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE)
        {
            /* add terminating zero */
            buffer[dwRead] = '\0';

            /* do something with data in buffer */
            printf("%s", buffer);
        }
    }

    DisconnectNamedPipe(hPipe);
}

return 0;

}

客户代码:

#include<stdio.h>
#include<windows.h>
#include<iostream>
#include<stdlib.h> 
using namespace std;
void fun()
{
     HANDLE hPipe;
    DWORD dwWritten;


    hPipe = CreateFile(TEXT("\\\\.\\pipe\\Pipe"), 
                       GENERIC_WRITE, 
                       0,
                       NULL,
                       OPEN_EXISTING,
                       0,
                       NULL);
    if (hPipe != INVALID_HANDLE_VALUE)
    {
        WriteFile(hPipe,
                  "Hello Pipe",
                  11,   // = length of string + terminating '\0' !!!
                  &dwWritten,
                  NULL);

        CloseHandle(hPipe);
    }

}
int main(void)
{
   int a = 5;
   cout<<a;
   for(int i = 0; i<a; i++)
   {
    fun();
    Sleep(2000);
   }

    return (0);
}

这只是一个示例程序,实际上我的客户端是一个非常大的应用程序,它有很多功能,我不想对其进行任何重大更改。每当触发特定功能时,它都应将数据传递给服务器。数据的类型是结构。在服务器中接收数据后,我想将该数据写入文本文件(将其转换为 json 格式后)。

那么如何让我的客户端在将数据写入管道之前检查服务器是否已完成从管道读取数据?客户端不应永远等待服务器,因为它会影响我的客户端应用程序。它应该等待指定的时间间隔。还要传递结构,我应该使用 BYTE MODE 还是 MESSAGE MODE?

【问题讨论】:

  • 如何阻止客户端直到读取操作完成 - 因为您使用同步句柄,这将由系统自动完成。 如何检查.. 你不需要检查这个。这根本不可能。您的任务 - 打开文件并向其中写入数据。如果你不想阻塞 - 使用异步句柄
  • 如果我不使用 sleep(),我的服务器无法正确读取数据。如果它是自动完成的,那为什么我没有得到正确的输出?
  • 因为您的代码中有一些错误。当然不需要任何Sleep
  • 您的服务器逻辑也不正确 - ConnectNamedPipe 可以返回 false 并返回错误 ERROR_NO_DATA 但数据实际存在于管道中。在这种情况下,您也可以致电 ReadFile。可以和ERROR_PIPE_CONNECTED 调用错误ConnectNamedPipe - 再次 - 这是好的代码,在此之后需要ReadFile
  • 我是管道概念的新手,你能在答案中写下修改后的客户端和服务器代码吗?

标签: c++ windows ipc named-pipes


【解决方案1】:

下一行服务器代码的主要错误

if (ConnectNamedPipe(hPipe, NULL) != FALSE)

这里我们假设如果ConnectNamedPipe 返回FALSE 它会失败。但即使对于同步管道句柄也是如此:

否则,ConnectNamedPipe 返回零,GetLastError 返回 ERROR_NO_DATA 如果前一个客户端已关闭其句柄或 ERROR_PIPE_CONNECTED 如果它没有关闭它的句柄。

客户端可以连接(调用CreateFile之后服务器调用CreateNamedPipeW之前调用ConnectNamedPipe。正是在这种情况下,驱动程序(npfs) 可以在 FSCTL_PIPE_LISTEN (ConnectNamedPipe) 请求中返回 2 个状态:

  1. STATUS_PIPE_CONNECTED(翻译成ERROR_PIPE_CONNECTED)如果 客户端已经连接(在FSCTL_PIPE_LISTEN 之前)但还没有 关闭自己的句柄)
  2. STATUS_PIPE_CLOSING(翻译成ERROR_NO_DATA)客户端有 关闭了它的把手。但他可以在此之前调用WriteFile,并且管道中的一些数据确实存在。

在调用CreateNamedPipeW 时也使用NMPWAIT_USE_DEFAULT_WAIT - 这里必须是实际超时。这个常量在调用WaitNamedPipeW 中使用。

下一个代码可以测试同步管道客户端/服务器。 (当然最好使用异步 i/o)

ULONG WINAPI ct(void* name)
{
    if (WaitNamedPipeW((PCWSTR)name, NMPWAIT_USE_DEFAULT_WAIT))
    {
        MessageBoxW(0, 0, L"client delay #1", 0);// for debug

        HANDLE hPipe = CreateFileW((PCWSTR)name, 
            GENERIC_WRITE, 
            0,
            NULL,
            OPEN_EXISTING,
            0,
            NULL);

        if (hPipe != INVALID_HANDLE_VALUE)
        {
            static WCHAR str[] = L"Hello Pipe";

            DWORD dwWritten;

            WriteFile(hPipe, str,
                sizeof(str),
                &dwWritten,
                NULL);

            MessageBoxW(0, 0, L"client delay #2", 0);// for debug

            CloseHandle(hPipe);
        }
    }

    return GetLastError();
}

void sc()
{
    char buffer[1024];

    static WCHAR name[] = L"\\\\.\\pipe\\Pipe";

    HANDLE hPipe = CreateNamedPipeW(name,
        PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
        1,
        1024 * 16,
        1024 * 16,
        INFINITE,
        NULL);

    if (hPipe != INVALID_HANDLE_VALUE)
    {
        int n = 2;
        do
        {
            CloseHandle(CreateThread(0, 0, ct, name, 0, 0));
            MessageBoxW(0, 0, L"Server delay", 0);// for debug

            switch (ConnectNamedPipe(hPipe, NULL) ? NOERROR : GetLastError())
            {
            case NOERROR:
            case ERROR_PIPE_CONNECTED: // STATUS_PIPE_CONNECTED
            case ERROR_NO_DATA: // STATUS_PIPE_CLOSING

                DWORD dwRead;
                while (ReadFile(hPipe, buffer, sizeof(buffer), &dwRead, NULL))
                {
                    /* do something with data in buffer */
                    printf("%.*S", dwRead, buffer);
                }

                DisconnectNamedPipe(hPipe);
                break;
            }
        } while (--n);

        CloseHandle(hPipe);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-12
    • 2016-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多