После возврата из функции ConnectNamedPipe сервер может выполнять чтение запросов с помощью функции ReadFile и запись ответов посредством функции WriteFile. Наконец, сервер должен вызвать функцию DisconnectNamedPipe, чтобы освободить дескриптор (экземпляра канала) для соединения с другим возможным клиентом.
Последняя функция, WaitNamedPipe, используется клиентами для синхронизации соединений с сервером. Функция осуществляет успешный возврат, когда на сервере имеется незавершенный вызов функции ConnectNamedPipe, указывающий на наличие доступного экземпляра именованного канала. Используя WaitNamedPipe, клиент имеет возможность убедиться в том, что сервер готов к образованию соединения, после чего может вызвать функцию CreateFile. Вместе с тем, вызов клиентом функции CreateFile может завершиться ошибкой, если в это же время другой клиент открывает экземпляр именованного канала или дескриптор экземпляра закрывается сервером. При этом неудачного завершения вызванной сервером функции ConnectNamedPipe не произойдет. Заметьте, что для функции WaitNamedPipe предусмотрен интервал ожидания, который, если он указан, отменяет значение интервала ожидания, заданного при вызове серверной функции CreateNamedPipe.
Подключение клиентов и серверов именованных каналов
Операции по подключению клиентов и серверов к именованным каналам выполняются в описанном ниже порядке. Сначала мы рассмотрим последовательность операций, выполняемых сервером, при помощи которых сервер создает соединение с клиентом, взаимодействует с клиентом до тех пор, пока тот не разорвет соединение (вынуждая функцию ReadFile вернуть значение FALSE), разрывает соединение на стороне сервера, а затем образует соединение с другим клиентом:
/* Последовательность операций при создании соединения с использованием именованного канала для сервера. */
hNp = CreateNamedPipe("\\\\.\\pipe\\my_pipe", …);
while (… /* Цикл продолжается вплоть до завершения работы сервера.*/) {
ConnectNamedPipe(hNp, NULL);
while (ReadFile(hNp, Request, …) {
…
WriteFile(hNp, Response, …);
}
DisconnectNamedPipe(hNp);
}
CloseHandle(hNp);
Перейдем к рассмотрению последовательности операций, выполняемых клиентом, в которой клиент прекращает выполнение после завершения работы, давая возможность подключиться к тому же экземпляру именованного канала другому клиенту. Как показано ниже, клиент может соединиться с сервером в сети, если ему известно сетевое имя сервера (ServerName):
/* Последовательность операций при создании соединения с использованием именованного канала для клиента. */
WaitNamedPipe("\\\\ServerName\\pipe\\my_pipe", NMPWAIT_WAIT_FOREVER);
hNp = CreateFile("\\\\ServerName\\pipe\\my_pipe", …);
while (…/*Цикл выполняется до тех пор, пока не прекратятся запросы.*/ {
WriteFile(hNp, Request, …);
…
ReadFile(hNp, Response);
}
CloseHandle (hNp); /* Разорвать соединение с сервером. */
Обратите внимание, что клиент и сервер состязаются за ресурсы. Прежде всего, клиентский вызов функции WaitNamedPipe завершится ошибкой, если именованный канал к этому моменту еще не был создан сервером; для краткости тестирование успешности выполнения в нашем примере опущено, однако оно включено в примеры программ, доступные на Web-сайте. Далее, в редких случаях вызов CreateFile может быть выполнен еще до того, как сервер вызовет функцию ConnectNamedPipe. В этом случае функция ConnectNamedPipe вернет серверу значение FALSE, однако взаимодействия посредством именованного канала по-прежнему будет функционировать надлежащим образом.
Экземпляр именованного канала является глобальным ресурсом, поэтому, когда клиент разрывает соединение с сервером, к нему может подключиться другой клиент.
Функции транзакций именованных каналов
На рис. 11.2 показана типичная конфигурация клиента, в которой клиент выполняет следующие операции:
• Открывает экземпляр канала, создавая долговременное соединение с сервером и занимая экземпляр канала.
• Периодически посылает запросы и ожидает получения ответов.
• Закрывает соединение.
Встречающуюся здесь последовательность вызовов функций WriteFile и ReadFile можно рассматривать как единую клиентскую транзакцию, и Windows предоставляет соответствующую функцию для каналов сообщений:
BOOL TransactNamedPipe(HANDLE hNamedPipe, LPVOID lpWriteBuf, DWORD cbWriteBuf, LPVOID lpReadBuf, DWORD cbReadBuf, LPDWORD lpcbRead, LPOVERLAPPED lpOverlapped)
Смысл всех параметров здесь должен быть ясен, поскольку данная функция сочетает в себе функции WriteFile и ReadFile, применяемые к дескриптору именованного канала. Указываются как выходной, так и входной буфер, а разыменованный указатель lpcbRead предоставляет размер сообщения. Перекрывающиеся операции (глава 14) возможны, однако в более типичных случаях функция ожидает ответа.
Читать дальше