Как заставить клиента проверять, завершен ли сервер с операцией чтения из канала перед записью данных в канал в с ++
Мне нужно, чтобы мой клиент сначала проверил, читает ли сервер данные из канала, если да, то подождите, пока сервер не будет выполнен, иначе запишите данные в канал. Если я не использую команду 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
) запрос:
STATUS_PIPE_CONNECTED
(переведено наERROR_PIPE_CONNECTED
) если клиент уже подключился (доFSCTL_PIPE_LISTEN
) и пока не закрывать собственную ручку)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);
}
}