З’єднання клієнта з каналом
Перед з’єднанням клієнт повинен перевірити доступність одного з примірників каналу. Для цього використовують функцію WaitNamedPipe():
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // вказівник на ім’я каналу
DWORD nTimeOut // інтервал очікування в мілісекундах
);
Ця функція чекає, поки з’явиться вільний примірник каналу (за умови, що серверний процес викликав функцію ConnectNamedPipe()) або вичерпається інтервал очікування. Параметри функції:
lpNamedPipeName ім’я каналу у форматі: \\servername\pipe\pipename
nTimeOut визначає час очікування на з’єднання з каналом в мілісекундах або приймає одне із значень:
NMPWAIT_USE_DEFAULT_WAIT Тайм-аут визначається функцією сервера CreateNamedPipe().
NMPWAIT_WAIT_FOREVER Функція не повертається, поки не стане доступним примірник каналу.
Після цього клієнт може викликати функцію CreateFile(), якій передається ім’я іменованого каналу в одному із вищенаведених форматів. При роботі з каналом слід звернути увагу на наступні параметри:
dwDesiredAccess може бути одним із (або комбінацією) таких значень:
GENERIC_READ дозволяє читання з каналу;
GENERIC_WRITE дозволяє запис в канал.
dwShareMode визначає режим спільного використання каналу і дорівнює нулю для заборони спільного доступу або комбінації таких значень:
FILE_SHARE_READ дозволяє спільне читання з каналу;
FILE_SHARE_WRITE дозволяє спільний запис в канал.
Якщо клієнт і сервер запущені на різних комп’ютерах і параметри безпеки задаються за умовчанням, то повинні бути задані однакові імена і паролі користувачів, оскільки канал належить користувачу, який його створив. Для загального доступу до каналу потрібно відповідним чином задати атрибути безпеки.
Обмін даними через іменований канал
Для обміну даними через іменований канал сервер і клієнт зазвичай використовують функції WriteFile(), ReadFile().
Крім цього є деякі специфічні функції для роботи з іменованим каналом. Зокрема, можна використовувати функцію TransactNamedPipe(), яка поєднує в одній мережевій операції функції відправки запиту і читання відповіді (фактично замінює дві функції WriteFile() і ReadFile()).
Функція CallNamedPipe() призначена для одноразової транзакції із використанням іменованого каналу і зручна для використання клієнтом. Вона поєднує в одній операції з’єднання з каналом, запис в канал, читання з каналу і закриття каналу. Таким чином, функція обслуговує один запит клієнта до сервера і звільняє канал для інших клієнтів.
Функція PeekNamedPipe() дає можливість перевірити наявність даних в каналі і визначити їх кількість.
Від’єднання сервера від каналу
Від’єднання сервера від каналу здійснюється за допомогою функції DisconnectNamedPipe(), яка відключає примірник іменованого каналу від клієнтського процесу.
BOOL DisconnectNamedPipe(
HANDLE hNamedPipe // дескриптор каналу
);
Після цього сервер може з’єднатися з іншим клієнтським процесом, використовуючи функцію ConnectNamedPipe() і наявний у нього дескриптор.
Завдання для виконання
1. Написати консольну програму клієнт-сервер із використанням іменованого каналу. Сервер створює іменований канал і переходить в режим прийому запитів від клієнта. Одержаний у вигляді текстового рядка запит виводить на екран. Клієнт з’єднується з іменованим каналом на заданому хості, приймає з клавіатури текстовий запит і відправляє його серверу. Ім’я хоста задається в командному рядку при запуску клієнта.
2. Змінити програму з п. 1 таким чином, щоб сервер після прийому запиту клієнта відправляв відповідь клієнту, а клієнт виводив його на екран.
3. Змінити програму з п. 2 таким чином, щоб клієнт і сервер могли обмінюватися повідомленнями багаторазово. Після одержання кожного повідомлення клієнт (сервер) виводить його на екран і переходить в режим введення з клавіатури нового повідомлення. Робота клієнта (сервера) завершується при введенні з клавіатури команди $quit.