Как заставить клиента проверять, завершен ли сервер с операцией чтения из канала перед записью данных в канал в с ++

Мне нужно, чтобы мой клиент сначала проверил, читает ли сервер данные из канала, если да, то подождите, пока сервер не будет выполнен, иначе запишите данные в канал. Если я не использую команду 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).

Итак, как я могу заставить своего клиента проверить, завершил ли сервер чтение данных из канала перед записью данных в канал? Клиент не должен ждать сервера вечно, так как это повлияет на мое клиентское приложение. Следует подождать указанный промежуток времени. Кроме того, чтобы передать структуру, какой режим трубы я должен использовать РЕЖИМ БАЙТА или РЕЖИМ СООБЩЕНИЯ?

1 ответ

Основная ошибка в следующей строке кода сервера

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

здесь мы предполагаем, что если ConnectNamedPipe вернуть FALSE это не удалось. но это не так даже для синхронного дескриптора трубы:

Иначе, ConnectNamedPipe возвращает ноль, и GetLastError возвращается ERROR_NO_DATA если предыдущий клиент закрыл свой дескриптор или ERROR_PIPE_CONNECTED если он не закрыл свою ручку.

клиент может подключиться (позвонить CreateFile) после вызова сервера CreateNamedPipeW но перед звонком ConnectNamedPipe, именно в этом случае драйвер (npfs) может вернуть 2 статуса на FSCTL_PIPE_LISTEN (ConnectNamedPipe) запрос:

  1. STATUS_PIPE_CONNECTED (переведено на ERROR_PIPE_CONNECTED) если клиент уже подключился (до FSCTL_PIPE_LISTEN) и пока не закрывать собственную ручку)
  2. STATUS_PIPE_CLOSING (переведено на ERROR_NO_DATA) клиент закрыл свою ручку но он может позвонить WriteFile до этого и некоторые данные в трубе действительно существуют.

также использовать NMPWAIT_USE_DEFAULT_WAIT в вызове CreateNamedPipeW - здесь должен быть фактический тайм-аут. эта константа используется в вызове WaitNamedPipeW вместо.

клиент / сервер синхронного канала может быть проверен следующим кодом. (конечно, гораздо лучше использовать асинхронный ввод / вывод)

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);
    }
}
Другие вопросы по тегам