Выбрать главу

Подключение клиентов и серверов именованных каналов

Операции по подключению клиентов и серверов к именованным каналам выполняются в описанном ниже порядке. Сначала мы рассмотрим последовательность операций, выполняемых сервером, при помощи которых сервер создает соединение с клиентом, взаимодействует с клиентом до тех пор, пока тот не разорвет соединение (вынуждая функцию 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) возможны, однако в более типичных случаях функция ожидает ответа.

Функция TransactNamedPipe удобна в использовании, однако, как показывает рис. 11.2, она требует создания постоянного соединения, что ограничивает число возможных клиентов[32].

Ниже приводится прототип второй клиентской вспомогательной функции.  

BOOL CallNamedPipe(LPCTSTR lpPipeName, LPVOID lpWriteBuf, DWORD cbWriteBuf, LPVOID lpReadBuf, DWORD cbReadBuf, LPDWORD lpcbRead, DWORD dwTimeOut) 

Функция CallNamedPipe не требует образования постоянного соединения; вместо этого она создает временное соединение, объединяя в себе выполнение следующей последовательности операций:

CreateFile

WriteFile

ReadFile

CloseHandle

Преимуществом такого способа является лучшее использование канала за счет снижения накладных расходов системных ресурсов на один запрос.