как заставить клиента проверять, завершен ли сервер с операцией чтения из канала перед записью данных в канал в с ++ - PullRequest
0 голосов
/ 02 июля 2018

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

0 голосов
/ 02 июля 2018

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

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);
    }
}
...