如何使客户端在使用c ++将数据写入管道之前先检查服务器是否已完成管道读取操作

时间:2018-07-02 09:00:48

标签: c++ windows ipc named-pipes

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

  

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

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

服务器代码:

#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?

1 个答案:

答案 0 :(得分:1)

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

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

这里我们假设,如果ConnectNamedPipe返回FALSE,它将失败。但这甚至对于同步管道句柄也不是这样:

  

否则,ConnectNamedPipe返回零,而GetLastError返回   ERROR_NO_DATA(如果先前的客户已关闭其句柄或   ERROR_PIPE_CONNECTED(如果尚未关闭手柄)。

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

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

还要在呼叫NMPWAIT_USE_DEFAULT_WAIT中使用CreateNamedPipeW-这必须是实际超时。该常量在调用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);
    }
}