Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Перенаправление стандартного ввода-вывода




Анонимные каналы часто используются для перенаправления стандартного ввода-вывода. Чтобы подробнее разобраться с этим вопросом, сначала кратко рассмотрим стандартные средства ввода-вывода, используемые в языке С++. Компилятор языка C++ фирмы Microsoft содержит стандартную библиотеку, которая поддерживает три варианта функций стандартного ввода-вывода. Описания этих функций находятся в следующих заголовочных файлах: <stdio.h>, <iostream.h> и <conio.h>. Функции ввода-вывода, которые описаны в заголовке <stdio.h>, обеспечивают ввод-вывод в следующие стандартные потоки:

stdin – стандартный файл ввода;

stdout – стандартный файл вывода;

stderr – файл вывода сообщений об ошибках. Эти функции составляют стандартную библиотеку ввода-вывода языка С. Функции и операторы ввода-вывода, которые описаны в заголовке <iostream.h>, обеспечивают ввод-вывод в стандартные потоки ввода-вывода cin, cout, cerr. Эти функции составляют стандартную библиотеку ввода-вывода языка С++. При создании консольного процесса или при распределении консоли приложением с графическим интерфейсом, стандартные потоки ввода-вывода связываются с дескрипторами, которые заданы в полях hStdInput, hStdOutput и hStdError структуры типа STARTUPINFO. Поэтому, если в эти поля будут записаны соответствующие дескрипторы анонимного канала, то для передачи данных по анонимному каналу можно использовать функции стандартного ввода-вывода. Такая процедура называется перенаправлением стандартного ввода-вывода.

Функции ввода-вывода, которые поддерживаются заголовком <conio.h>, отличаются от функций стандартной библиотеки ввода-вывода языка С только тем, что они всегда связываются с консолью. Поэтому эти функции можно использовать для ввода-вывода на консоль даже в случае перенаправления стандартного ввода-вывода.

Ниже приведены программы, в которых стандартный ввод-вывод перенаправляется в анонимный канал, а для обмена данными по анонимному каналу используются перегруженные операторы ввода-вывода. Пример включает программы следующих процессов: два процесса клиента, которые обмениваются данными по анонимному каналу, и процесс сервер, который создает клиентов и передает им дескрипторы анонимного канала через поля структуры STARTUPINFO. Сначала приведем программы, которые описывают процессы клиенты.

// Пример обмена данными по анонимному каналу,

// используя перенаправленные стандартные потоки ввода-вывода.

// Дескрипторы анонимного канала передаются через поля структуры STARTUPINFO.

#include <windows.h> #include <conio.h> #include <iostream.h>

int main() {

// события для синхронизации обмена данными

HANDLE hReadFloat, hReadText;

char lpszReadFloat[] = "ReadFloat";

char lpszReadText[] = "ReadText";

// открываем события hReadFloat = CreateEvent(NULL, FALSE, FALSE, lpszReadFloat); hReadText = CreateEvent(NULL, FALSE, FALSE, lpszReadText);

// ждем команды о начале записи в анонимный канал _cputs("Press any key to start communication.\n"); _getch();

// пишем целые числа в анонимный канал for (int i = 0; i < 5; ++i) {

Sleep(500); cout << i << endl;

}

// ждем разрешение на чтение дробных чисел из канала WaitForSingleObject(hReadFloat, INFINITE);

// читаем дробные числа из анонимного канала for (int j = 0; j < 5; ++j) {

float nData;

cin >> nData;

_cprintf("The number %2.1f is read from the pipe.\n", nData);

}

}

// отмечаем, что можно читать текст из анонимного канала SetEvent(hReadText);

// теперь передаем текст cout << "This is a demo sentence." << endl;

// отмечаем конец передачи cout << '\0' << endl;

_cputs("The process finished transmission of data.\n");

_cputs("Press any key to exit.\n");

_getch();

CloseHandle(hReadFloat); CloseHandle(hReadText);

return 0;

 

Листинг 5. Пример обмена данными по анонимному каналу.

// используя перенаправленные стандартные потоки ввода-вывода.

// Дескрипторы анонимного канала передаются через поля структуры STARTUPINFO.

#include <windows.h> #include <conio.h> #include <iostream.h>

int main() {

// события для синхронизации обмена данными HANDLE hReadFloat, hReadText; char lpszReadFloat[] = "ReadFloat"; char lpszReadText[] = "ReadText";

// открываем события hReadFloat = CreateEvent(NULL, FALSE, FALSE, lpszReadFloat); hReadText = CreateEvent(NULL, FALSE, FALSE, lpszReadText);

// читаем целые числа из анонимного канала for (int i = 0; i < 5; ++i) {

int nData;

cin >> nData;

_cprintf("The number %d is read from the pipe.\n", nData); }

// разрешаем читать дробные числа из анонимного канала SetEvent(hReadFloat);

// пишем дробные числа в анонимный канал

for (int j = 0; j < 5; ++j)

}

{

Sleep(500);

cout << (j*0.1) << endl; }

// ждем разрешения на чтение текста

WaitForSingleObject(hReadText, INFINITE);

_cputs("The process read the text: ");

// теперь читаем текст char lpszInput[80]; do {

Sleep(500);

cin >> lpszInput;

_cputs(lpszInput);

_cputs(" ");

}

while (*lpszInput!= '\0');

_cputs("\nThe process finished transmission of data.\n");

_cputs("Press any key to exit.\n");

_getch();

CloseHandle(hReadFloat); CloseHandle(hReadText);

return 0;

Теперь приведем программу, которая описывает сервер анонимного канала. Эта программа просто создает двух клиентов анонимного канала и прекращает свою работу.

Листинг 6. Пример процесса сервера анонимного канала.

// Сервер создает анонимный канал, а затем два процесса клиента

// анонимного канала, которые обмениваются между собой данными по этому каналу.

// Дескрипторы анонимного канала передаются клиентам через поля структуры STARTUPINFO.

#include <windows.h> #include <conio.h>

int main() {

char lpszComLine1[80] = "C:\\Client1.exe"; // имя первого клиента

char lpszComLine2[80] = "C:\\Client2.exe"; // имя второго клиента

STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE hWritePipe, hReadPipe; SECURITY_ATTRIBUTES sa;

// устанавливает атрибуты защиты канала
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL; // защита по умолчанию
sa.bInheritHandle = TRUE; // дескрипторы наследуемые

// создаем анонимный канал if(!CreatePipe(

&hReadPipe, // дескриптор для чтения
&hWritePipe, // дескриптор для записи
&sa, // атрибуты защиты по умолчанию,

// дескрипторы наследуемые
0)) // размер буфера по умолчанию

{

_cputs("Create pipe failed.\n"); _cputs("Press any key to finish.\n"); _getch();

return GetLastError(); }

// устанавливаем атрибуты нового процесса ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO);

// использовать стандартные дескрипторы si.dwFlags = STARTF_USESTDHANDLES;

// устанавливаем стандартные дескрипторы si.hStdInput = hReadPipe; si.hStdOutput = hWritePipe; si.hStdError = hWritePipe;

// запускаем первого клиента if (!CreateProcess(

NULL, // имя процесса

lpszComLine1, // командная строка

NULL, // атрибуты защиты процесса по умолчанию

NULL, // атрибуты защиты первичного потока по умолчанию

TRUE, // наследуемые дескрипторы текущего процесса

// наследуются новым процессом
CREATE_NEW_CONSOLE, // создаем новую консоль
NULL, // используем среду окружения процесса предка

NULL, // текущий диск и каталог как и в процессе предке

&si, // вид главного окна - по умолчанию

&pi // здесь будут дескрипторы и идентификаторы

// нового процесса и его первичного потока)) {

_cputs("Create process failed.\n"); _cputs("Press any key to finish.\n"); _getch();

return GetLastError(); }

// закрываем дескрипторы первого клиента CloseHandle(pi.hProcess); CloseHandle(pi.hThread);

// запускаем второго клиента if (!CreateProcess(

NULL, // имя процесса

lpszComLine2, // командная строка

NULL, // атрибуты защиты процесса по умолчанию

NULL, // атрибуты защиты первичного потока по умолчанию

TRUE, // наследуемые дескрипторы текущего процесса

// наследуются новым процессом
CREATE_NEW_CONSOLE, // создаем новую консоль
NULL, // используем среду окружения процесса предка

NULL, // текущий диск и каталог как и в процессе предке

&si, // вид главного окна - по умолчанию

&pi // здесь будут дескрипторы и идентификаторы

// нового процесса и его первичного потока)

) {

_cputs("Create process failed.\n");

_cputs("Press any key to finish.\n");

_getch();

return GetLastError(); }

// закрываем дескрипторы второго клиента CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

// закрываем дескрипторы канала

CloseHandle(hReadPipe);

CloseHandle(hWritePipe);

_cputs("The clients are created.\n");

_cputs("Press any key to exit.\n");

_getch();

return 0; }

Задание на выполнение

1. Изучить теоретический материал, посвященный обмену данных между процессами посредством анонимных каналов.

2. Используя представленные в работе примеры программ, реализовать приложения, демонстрирующие использование анонимных каналов при межпроцессном обмене.

3. В соответствии с заданным вариантом разработать приложение для консольных процессов, которые обмениваются сообщениями по анонимному каналу.

4. Результаты работы представить преподавателю в виде отчета и продемонстрировать функционирующее приложение.

Варинты заданий

1. Написать программы трёх консольных процессов Server и Client, которые обмениваются сообщениями по анонимному каналу.

Сообщения бывают двух типов:

1. сообщения первого типа содержат размер буфера экрана и размер курсора для консольного приложения;

2. сообщения второго типа содержат массив символов.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Запрашивает размер буфера экрана и размер курсора.

− Размер массива вводится с консоли.

− Запускает процесс Client.

− Получает и передает от процесса- Client по анонимным каналам массив символов. Выводит полученные результаты и переданные массив на консоль.

− Передача сообщения первого типа инициируется нажатием правой кнопки мыши. Передача сообщения второго типа инициируется посредством нажатия клавиши “G” на клавиатуре.

− Закончить работу, после нажатия левой клавиши мыши.

Процесс- Client, который выполняет следующие действия.

− Генерирует символы и передает их по анонимному каналу процессу-серверу.

− Получает по анонимному каналу размер буфера экрана и размер курсора от Server и устанавливает их;

− Передача сообщения второго типа инициируется нажатием правой кнопки.

− Выводит сгенерированный массив на консоль.

− Заканчивает работу.

2. Написать программы трёх консольных процессов Server и Client, Part которые обмениваются сообщениями по анонимному каналу.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Создает массив, для хранения вещественных чисел.

− Размер массива вводится с консоли.

− Запускает процесс Client.

− Запускает процесс Part.

− Передёт размер массива процессам Client, Part. Получает и передает по анонимным каналам массив чисел. Выводит полученные результаты и переданные массив на консоль. Элементы массива передаются посимвольно.

− Повторяет запрос массива от процесса- Client посредством нажатия любой клавиши на клавиатуре.

− Закончить работу после нажатия клавиши - “Enter”.

Процесс- Client, который выполняет следующие действия.

− Запрашивает у пользователя размер буфера экрана и размер курсора консоли и устанавливает их.

− Генерирует вещественные числа и передает их по анонимному каналу процессу-серверу.

− Выводит сгенерированный массив на консоль.

Процесс-Part, который выполняет следующие действия.

− Получает массив чисел по анонимному каналу от процесса-сервера

− Запрашивает число вещественные числа N и M (N < M).

− Определяет какие из чисел попали в отрезок [N,M], передаёт их по анонимному каналу процессу-серверу.

− Передача массива инициируется нажатием левой кнопки мыши.

− Выводит полученные числа на консоль.

3. Написать программы трёх консольных процессов Server и Client, Sum которые обмениваются сообщениями по анонимному каналу.

Одновременно сообщение может передаваться только одним из процессов.

Процесс -Server, который выполняет следующие действия.

− Размер массива вводится с консоли.

− Запускает процесс Client.

− Запускает процесс Sum.

− Передача размера массива инициируется нажатием правой кнопки мыши.

− Получает и передаёт по анонимным каналам массив чисел. Выводит полученные результаты и переданные массив на консоль.

− Передача массив процессу Sum инициируется нажатием левой кнопки мыши. Элементы массива передаются посимвольно.

− Повторяет запрос массива от процесса- Client посредством нажатия любой клавиши на клавиатуре.

− Закончить работу после нажатия клавиши - “Q”.

Процесс -Client, который выполняет следующие действия.

− Запрашивает у пользователя размер буфера экрана и размер курсора и устанавливает их.

− Получает от сервера размер массива.

− Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Выводит сгенерированный массив на консоль.

Процесс -Sum, который выполняет следующие действия.

− Получает массив символов по анонимному каналу от процесса-сервера

− Запрашивает с консоли число N.

− Вычисляет сумму квадратов чисел масcива, больших N

− Передаёт cумму по анонимному каналу процессу-серверу.

− Передача сообщения инициируется двойным нажатием левой кнопки.

− Выводит полученное число на консоль.

4. Написать программы двух консольных процессов Server, Mult, Sum которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат размер курсора и цвет фона консоли;

2. сообщения второго типа содержат массив символов.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Запрашивает размер массива.

− Запрашивает у пользователя размер курсора и цвет фона консоли;

− Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Запускает процессы Mult, Sum.

− Получает и передает по анонимным каналам массив символов. Выводит полученные результаты и переданные массив на консоль.

− Передача первого сообщения инициируется нажатием левой кнопки мыши. В этом случае данный процесс передаёт размер курсора и цвет фона консоли процессу Sum.

− Передача второго сообщения инициируется нажатием клавиши «G» на клавиатуре. В этом случае данный процесс передаёт массив чисел другим процессам.

− Запрашивает результат от процессов- Mult, Sum посредством нажатия клавиши «S» на клавиатуре.

− Закончить работу после нажатия клавиши - “Enter”

Процесс -Sum, который выполняет следующие действия.

− Получает массив чисел от сервера.

− Получает размер курсора и цвет фона консоли, устанавливает их;

− Вычисляет сумму чисел массива.

− Передаёт число серверу.

− Выводит сумму на консоль.

Процесс -Mult, который выполняет следующие действия.

− Получает массив чисел от сервера.

− Вычисляет произведение чисел массива

− Передаёт число серверу.

− Выводит сумму на консоль

5. Написать программы трёх консольных процессов Server и Client, Sort которые обмениваются сообщениями по анонимному каналу.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Размер массива вводится с консоли.

− Запускает процесс Client.

− Запускает процесс Sort.

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

− Получает по анонимным каналам массив чисел. Выводит полученные результаты и переданные массивы на консоль.

− Запрашивает массив от процесса Client посредством нажатия любой клавиши на клавиатуре.

− Получает массив от процесса Sort;

− Закончить работу после нажатия клавиши - “Q”.

Процесс -Client, который выполняет следующие действия.

− Получает размер массива.Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Выводит сгенерированный массив на консоль.

− Передаёт массив процессу Server. Элементы массива передаются посимвольно.

Процесс -Sort, который выполняет следующие действия.

− Получает массив символов по анонимному каналу от процесса Server;

− Сортирует массив;

− Запрашивает у пользователя размер окна консоли и размер курсора консоли и устанавливает их.

− Передаёт отсортированный массив по анонимному каналу процессу.

− Передача сообщения Server инициируется двойным нажатием левой кнопки мыши.

− Элементы массива передаются посимвольно.

− Выводит полученный массив на консоль.

6. Написать программы трёх консольных процессов Server и Client, Hight которые обмениваются сообщениями по анонимному каналу.

Одновременно сообщение может передаваться только одним из процессов.

Процесс -Server, который выполняет следующие действия.

− Создает массив, для хранения вещественных чисел.

− Размер массива вводится с консоли.

− Запрашивает у пользователя размер курсора экрана и размер буфера окна;

− Запускает процесс Client и процесс Hignt.

− Получает и передает по анонимным каналам массив чисел. Выводит полученные результаты и переданные массив на консоль. Элементы массива передаются посимвольно.

− Передача массива процессу Client, размер курсора экрана и размер буфера окна инициируется нажатием левой кнопки мыши.

− Закончить работу после нажатия клавиши – “E”.

Процесс- Client, который выполняет следующие действия.

− Генерирует вещественные числа.

− Получает размер курсора экрана и размер буфера окна и устанавливает их.

− Передаёт массив процессу Server по анонимному каналу, посредством нажатия любой клавиши на клавиатуре.

− Выводит сгенерированный массив на консоль.

Процесс -Hignt, который выполняет следующие действия.

− Получает массив чисел по анонимному каналу от процесса- Server, посредством нажатия правой клавиши мыши.

− Запрашивает вещественное число N.

− Определяет какие из чисел >N передаёт их по анонимному каналу процессу-серверу.

− Выводит полученные числа на консоль.

7. Написать программы трёх консольных процессов Server и Client, Simple которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат цвет фона и размер курсора для консольного приложения;

2. сообщения второго типа содержат массив чисел.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Размер массива вводится с консоли.

− Запрашивает в произвольной форме (лучше, используя палитру цветов) у пользователя цвета фона и размер курсора консоли;

− Запускает процесс Client.

− Запускает процесс Simple.

− Получает и передает по анонимным каналам массив чисел. Выводит полученные и переданные числа на консоль.

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

− Передача сообщения второго типа инициируется нажатием левой кнопки мыши. Элементы массива передаются посимвольно.

− Закончить работу после нажатия клавиши - “Ctrl”.

Процесс- Client, который выполняет следующие действия.

− Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Получает цвет фона и размер курсора от Server и устанавливает их;

− Размер массива запрашивается с консоли.

− Передача сообщения второго типа инициируется нажатием кнопки на клавиатуре.

− Выводит сгенерированные числа на консоль. -

Процесс -Simple, который выполняет следующие действия.

− Получает массив числа по анонимному каналу от процесса-сервера

− Находит и передает простые числа по анонимному каналу процессу-серверу.

− Передача сообщения второго типа инициируется двойным нажатием левой кнопки мыши. Элементы массива передаются посимвольно.

− Выводит полученные числа на консоль.

8. Написать программы трёх консольных процессов Server и Client, Small которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат цвет фона и текста консольного приложения;

2. сообщения второго типа содержат массив чисел.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Размер массива вводится с консоли.

− Запрашивает (использовать палитру цветов) у пользователя цвета фона и цвет текста консоли;

− Запускает процесс Client.

− Запускает процесс Small.

− Передает по анонимным каналам размер массива;

− Получает и передает по анонимным каналам массив чисел. Выводит полученные и переданные числа на консоль.

− Передача сообщения первого типа инициируется нажатием правой кнопки.

− Передача сообщения второго типа инициируется нажатием кнопки. Alt

− Элементы массива передаются посимвольно.

− Закончить работу

Процесс- Client, который выполняет следующие действия.

− Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Получает цвет фона и размер курсора от Server и устанавливает их;

− Передача сообщения второго типа инициируется нажатием левой кнопки.

− Выводит сгенерированные числа на консоль.

Процесс- Small, который выполняет следующие действия.

- Получает цвет фона и размер курсора от Server и устанавливает их;

− Получает размер массива и массив чисел по анонимному каналу от процесса-сервера

− Запрашивает вещественное число N.

− Определяет какие из чисел >0 и <N передаёт их по анонимному каналу процессу-серверу.

− Передача сообщения второго типа инициируется двойным нажатием левой кнопки.

− Элементы массива передаются посимвольно.

- Выводит полученные числа на консоль.

9. Написать программы трёх консольных процессов Server и Client, Alfavit которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат цвет фона и координаты позиции курсора экрана для вывода с этой позиции символов для консольного приложения;

2. сообщения второго типа содержат массив символов.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Размер массива вводится с консоли.

− Запрашивает в произвольной форме (лучше, используя палитру цветов) у пользователя цвета фона, координаты позиции курсора экрана;

− Запускает процесс Client.

− Запускает процесс Alfavit.

− Получает и передает по анонимным каналам массив символов. Выводит полученные и переданные символы на консоль.

− Передача сообщения первого типа процессу Alfavit инициируется нажатием кнопки на клавиатуре.

− Передача сообщения второго типа инициируется двойным нажатием правой кнопки мыши. Элементы массива передаются посимвольно.

− Закончить работу после нажатия клавиши - “Esc”.

Процесс- Client, который выполняет следующие действия.

− Получает размер массива от процесса Server

− Генерирует символы и передает их по анонимному каналу процессу-серверу.

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

− Символы передаются посимвольно.

− Выводит произведенные символы консоль.

Процесс -Alfavit, который выполняет следующие действия.

− Получает цвет фона и координаты позиции курсора от Server и устанавливает их;

− Получает массив символов по анонимному каналу от процесса-сервера.

− Определяет символы, принадлежащие латинскому алфавиту и передает их по анонимному каналу процессу-серверу.

− Выводит полученные символы на консоль, начина с позиции курсора.

− Передача сообщения второго типа инициируется нажатием правой кнопки мыши. Символы передаются посимвольно.

10. Написать программы трёх консольных процессов Server и Client, Figure которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат заголовок и координаты позиции курсора экрана для вывода с этой позиции символов для консольного приложения;

2. сообщения второго типа содержат массив символов, которые должны выводиться

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

- Размер массива вводится с консоли.

− Запрашивает заголовок и координаты позиции курсора экрана у пользователя;

− Запускает процесс Client.

− Запускает процесс Figure.

− Получает и передает по анонимным каналам массив символов. Выводит полученные и переданные числа на консоль.

− Передача сообщения первого типа инициируется нажатием кнопки на клавиатуре. В этом случае данный процесс установливает их на своей консоли и передать их другим процессам

− Передача сообщения второго типа инициируется нажатием правой кнопки мыши.

− Элементы массива предаются по одному.

− Закончить работу, после нажатия клавиши - “Esc”.

Процесс- Client, который выполняет следующие действия.

− Генерирует символы и передает их по анонимному каналу процессу-серверу.

− Получает координаты позиции курсора от Server и устанавливает их;

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

− Символы передаются посимвольно.

Выводит произведенные символы консоль, начина с позиции курсора.

Процесс -Figure, который выполняет следующие действия.

− Получает заголовок от Server и устанавливает его;

− Получает массив символов по анонимному каналу от процесса-сервера.

− Определяет цифры и передает их по анонимному каналу процессу-серверу.

− Передача сообщения второго типа инициируется двойным нажатием левой кнопки мыши;

− Элементы массива предаются по одному;

− Начиная с позиции курсора выводит полученные символы на консоль.

11. Написать программы двух консольных процессов Server, Mult, Sum которые обмениваются сообщениями
по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат размер буфера экрана и размер курсора;

2. сообщения второго типа содержат массив символов.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Запрашивает размер массив.

− Запрашивает у пользователя размер буфера экрана и размер курсора;

− Генерирует целые числа для массива и передает их по анонимному каналу другим процессам

− Запускает процессы Mult, Sum.

− Получает и передает по анонимным каналам массив символов. Выводит полученные результаты на консоль.

− Передача первого сообщения инициируется нажатием левой кнопки мыши. В этом случае данный процесс передаёт буфера экрана и позицию курсора процессу Sum.

− Передача второго сообщения инициируется нажатием клавиши «G» на клавиатуре. В этом случае данный процесс передаёт массив чисел другим процессам.

− Запрашивает результат от процессов- Mult, Sum посредством двойного нажатия кнопки мыши.

− Закончить работу, после нажатия клавиши - “Enter”

Процесс- Sum, который выполняет следующие действия.

− Получает массив чисел от сервера.

− Получает размер буфера экрана и размер курсора, устанавливает их;

− Вычисляет сумму чисел массива.

− Передаёт число серверу.

− Выводит сумму на консоль.

Процесс- Mult, который выполняет следующие действия.

− Получает массив чисел от сервера.

− Вычисляет произведение чисел массива

− Передаёт число серверу.

− Выводит сумму на консоль

12. Написать программы трёх консольных процессов Server и Client, Palindrom которые обмениваются сообщениями по анонимному каналу. Сообщения бывают двух типов:

1. сообщения первого типа содержат цвет символов экрана и размер курсора для консольного приложения;

2. сообщения второго типа содержат массив символов.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Создает массив, для хранения целых чисел.

− Размер массива вводится с консоли.

− Запрашивает у пользователя цвет символов экрана и размер курсора консоли;

− Запускает процесс Client.

− Запускает процесс Palindrom.

− Получает и передает по анонимным каналам массив символов. Выводит полученные результаты и переданные массив на консоль.

− Передача сообщения первого типа инициируется нажатием правой кнопки. В этом случае данный процесс передаёт их (параметры) другому процессу, который должен установить их (параметры) на своей консоли.

− Передача сообщения второго типа инициируется нажатием левой кнопки. Элементы массива передаются посимвольно клавиши мыши

− Закончить работу после нажатия клавиши.

Процесс- Client, который выполняет следующие действия.

− Генерирует целые числа и передает их по анонимному каналу процессу-серверу.

− Получает цвет текста от Server и устанавливает их;

− Размер массива запрашивается с консоли.

− Передача сообщения второго типа инициируется нажатием правой кнопки.

− Выводит сгенерированный массив на консоль.

− Закончить работу после нажатия клавиши мыши

Процесс -Palindrom, который выполняет следующие действия.

− Получает размер курсора от Server и устанавливает их;

− Получает массив символов по анонимному каналу от процесса-сервера

− Находит палиндром в строке и передает полином по анонимному каналу процессу-серверу.

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

− Выводит полученные палиндромы на консоль.

13. Написать программы консольных процессов Server, Produce и Consume для управления параллельным доступом процессов к массиву.

Одновременно сообщение может передаваться только одним из процессов.

Процесс- Server, который выполняет следующие действия.

− Запускает процесс Producer, которые производят элементы для массива.

− Запускает процесс Consumer, которые потребляют элементы из массива.

− Дает процессам Produce и Consumer команду на начало работы.

− Получает и передает по анонимным каналам целые числа от производителей и потребителей соответственно. Выводит полученные и переданные числа на консоль.

− Передача элементов массива инициируется двойным нажатием левой кнопки мыши.

− Закончить работу, после нажатия клавиши мыши

Процесс- Produce, который выполняет следующие действия:

− Запрашивает с консоли количество чисел для производства.

− Генерирует массив чисел и передает их по анонимному каналу процессу-серверу.

− Передача целых чисел начинается по команде сервера.

− Выводит произведенные целые числа на консоль.

− Процесс- Consume, который выполняет следующие действия.

− Получает числа по анонимному каналу от процесса-сервера.

− Выводит полученные целые числа на консоль.

− Количество чисел, которые должны быть потреблены, запрашивается с консоли.

− Отправка целых чисел начинается после нажатия клавиши мыши.

Контрольные вопросы

1. Перечислите основные способы взаимодействия процессов.

2. В чем отличие каналов от отображения файлов?

3. Перечислите основные параметры создания анонимных каналов.

4. В чем состоят недостатки анонимных каналов?

 

Лабораторная работа №7

Тема: Обмен данными по именованному каналу с сервером

 

Цель работы:

1. Изучение механизмов межпроцессного обмена в ОС семейства Windows.

2. Изучение функций для работы с именованными каналами.

3. В соответствии с заданным вариантом разработать приложение, реализующее обмен данными между процессами с помощью именованных каналов.

 

Краткое теоретическое введение

1. Создание именованных каналов

Работа с именованными каналами также как и работа с анонимными каналами требует совместного использования целого ряда функций. Поэтому сначала рассмотрены все функции, которые предназначены для работы с именованными каналами, а затем приведены несколько примеров, которые иллюстрируют использование этих функций.

Именованные каналы создаются процессом-сервером при помощи функции CreateNamedPipe, которая имеет следующий прототип:

HANDLE CreateNamedPipe (

LPCTSTR lpName, // имя канала

DWORD dwOpenMode, // атрибуты канала

DWORD dwPipeMode, // режим передачи данных

DWORD nMaxInstances, // максимальное количество экземпляров канала

DWORD nOutBufferSize, // размер выходного буфера

DWORD nInBufferSize, // размер входного буфера

DWORD nDefaultTimeOut, // время ожидания связи с клиентом

LPSECURITY_ATTRIBUTES lpPipeAttributes // атрибуты защиты

);

где параметры имеют следующие значения.

Параметр lpName указывает на строку, которая должна иметь вид:

\\.\pipe\<pipe_name>

Здесь точка (.) обозначает локальную машину, так как новый именованный канал всегда создается на локальной машине, слово pipe - фиксировано, а <pipe_name> обозначает имя канала, которое задается пользователем и нечувствительно к регистру.

Параметр dwOpenMode задает флаги, которые определяют направление передачи данных, буферизацию, синхронизацию обмена данными и права доступа к именованному каналу. Для определения направления передачи данных используются флаги:

PIPE_ACCESS_DUPLEX чтение и запись в канал,

PIPE_ACCESS_INBOUND клиент пишет, а сервер читает данные,

PIPE_ACCESS_OUTBOUND сервер пишет, а клиент читает данные.

Флаг, определяющий направление передачи данных по именованному каналу, должен совпадать для всех экземпляров одного и того же именованного канала. Для определения способа буферизации и синхронизации используются флаги:

FILE_FLAG_WRITE_THROUGH запрещает буферизацию при передаче данных по сети.

FILE_FLAG_OVERLAPPED разрешает асинхронную передачу данных по каналу.

Эти флаги могут быть разными для каждого экземпляра одного и того же именованного канала. Флаги для определения атрибутов защиты будут рассмотрены позднее.

Параметр dwPipeMode задает флаги, способ передачи данных по именованному каналу. Для определения способов чтения и записи данных в именованный канал используются флаги:

PIPE_TYPE_BYTE запись данных потоком,

PIPE_TYPE_MESSAGE запись данных сообщениями.

PYPE_READMODE_BYTE чтение данных потоком,

PYPE_READMODE_MESSAGE чтение данных сообщениями.

По умолчанию данные по именованному каналу предаются потоком. Флаги способов чтения и записи данных в именованный канал должны совпадать для всех экземпляров одного и того же именованного канала. Для определения синхронизации доступа к именованному каналу используются флаги:

PIPE_WAIT синхронная связь с каналом и обмен данными по каналу,

PIPE_NOWAIT асинхронная связь с каналом и обмен данными по каналу.

Эти флаги могут быть разными для каждого экземпляра именованного канала.

Параметр nMaxInstances определяет максимальное число экземпляров именованного канала, которое может находиться в пределах от 1 до PIPE_UNLIMITED_INSTANCES.

Параметры nOutBufferSize и nInBufferSize определяют соответственно размеры выходного и входного буферов для обмена данными по именованному каналу. Однако, эти значения рассматриваются операционными системами Windows только как пожелания пользователя, а сам выбор размеров буферов остается за операционной системой.

Параметр nDefaultTimeOut устанавливает время ожидания клиентом связи с сервером, если клиент вызывает функцию WaitNamedPipe, в которой интервал ожидания интервал ожидания задается по умолчанию.

При удачном завершение функция CreateNamedPipe возвращает значение дескриптор именованного канала, в случае неудачи - одно из двух значений:

INVALID_HANDLE_VALUE неудачное завершение,

ERROR_INVALID_PARAMETR значение параметра nMaxInstances больше, чем величина

PIPE_UNLIMITED_INSTANCES.

Для связи сервера с несколькими клиентами по одному именованному каналу сервер должен создать несколько экземпляров этого канала. Каждый экземпляр именованного канала создается вызовом функции CreateNamedPipe, в которой некоторые флаги должны быть установлены одинаково для всех экземпляров одного и того же именованного канала. Каждый новый вызов этой функции возвращает новый дескриптор на создаваемый экземпляр именованного канала.

 

2. Соединение сервера с клиентом

После того, как сервер создал именованный канал, он должен дождаться соединения клиента с этим каналом. Для этого сервер вызывает функцию

BOOL ConnectNamedPipe (

HANDLE hNamedPipe, // дескриптор канала

LPOVERLAPPED lpOverlapped // асинхронная связь

);

которая возвращает значение TRUE в случае успеха или значение FALSE в случае неудачи. Сервер может использовать эту функцию для связи с клиентом по каждому новому экземпляру именованного канала.

После окончания обмена данными с клиентом, сервер может вызвать функцию

BOOL DisconnectNamedPipe (

HANDLE hNamedPipe // дескриптор канала

);

которая возвращает значение TRUE в случае успеха или значение FALSE в случае неудачи. Эта функция разрывает связь сервера с клиентом. После этого клиент не может обмениваться данными с сервером по данному именованному каналу и поэтому любая операция доступа к именованному каналу со стороны клиента вызовет ошибку. После разрыва связи с одним клиентом, сервер снова может вызвать функцию ConnectNamedPipe, чтобы установить связь по этому же именованному каналу с другим клиентом.

 

3. Соединение клиентов с именованным каналом

Прежде чем соединяться с именованным каналом, клиент должен определить доступен ли какой-либо экземпляр этого канала для соединения. С этой целью клиент должен вызвать функцию:

BOOL WaitNamedPipe (

LPCTSTR lpNamedPipeName, // указатель на имя канала

DWORD nTimeOut // интервал ожидания

);

которая в случае успешного завершения возвращает значение TRUE, а в случае неудачи - FALSE. Параметры этой функции имеют следующие значения.

Параметр lpNamedPipeName указывает на строку, которая должна иметь вид

\\<server_name>\pipe\<pipe_name>

Здесь <server_name> обозначает имя компьютера, на котором выполняется сервер именованного канала.

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

NMPWAIT_USE_DEFAULT_WAIT интервал времени ожидания определяется значением параметра nDefaultTimeOut, который задается в функции CreateNamedPipe,

NMPWAIT_WAIT_FOREVER бесконечное время ожидания связи с именованным каналом.

Сделаем два важных замечания относительно работы функции WaitNamedPipe. Во-первых, если не существует экземпляров именованного канала с именем lpNamedPipe, то эта функция немедленно завершается неудачей, независимо от времени ожидания, заданного параметром nTimeOut. Во-вторых, если клиент соединяется с каналом до вызова сервером функции ConnectNamedPipe, то функция WaitNamedPipe возвращает значение FALSE и функция GetLastErrorвернет код ERROR_PIPE_CONNECTED. Поэтому функцию WaitNamedPipe нужно вызывать только после соединения сервера с каналом посредством функции ConnectNamedPipe.

После того как обнаружен свободный экземпляр канала, для того чтобы установить связь с этим каналом клиент должен вызвать функцию

HANDLE CreateFile (

LPCTSTR lpFileName, // указатель на имя канала

DWORD dwDesiredAccess, // чтение или запись в канал

DWORD dwShareMode, // режим совместного использования

LPSECURITY_ATTRIBUTES lpSecurity Attributes, // атрибуты защиты DWORD dwCreationDisposition, // флаг открытия канала

DWORD dwFlagsAndAttributes, // флаги и атрибуты

HANDLE hTemplateFile // дополнительные атрибуты

);

которая в случае успешного завершения возвращает дескриптор именованного канала, а в случае неудачи - значение INVALID_HANDLE_VALUE.

Параметры функции CreateFile могут принимать следующие значения, если эта функция используется для открытия именованного канала.

Параметр lpFileName должен указывать на имя канала, которое должно быть задано в том же формате, что и в функции WaitNamedPipe.

Параметр dwDesiredAccess может принимать одно из следующих значений:

0 разрешает получить атрибуты канала,

GENERIC_READ разрешает чтение из канала,

GENERIC_WRITE разрешает запись в канал.

Следует отметить, что функция CreateFileзавершается неудачей, если доступ к именованному каналу, заданный этими значениями, не соответствует значениям параметра dwOpenMode в функции CreateNamedPipe. Кроме того, в этом параметре программист может определить стандартные права доступа к именованному каналу. За более подробной информацией по этому вопросу нужно обратиться к MSDN.

Параметр dwShareMode определяет режим совместного использования именованного канала и может принимать значение 0, которое запрещает совместное использование именованного канала или любую комбинацию следующих значений:

FILE_SHARE_READ разрешает совместное чтение из канала,

FILE_SHARE_WRITE разрешает совместную запись в канал.

Параметр lpSecurityAttributes задает атрибуты защиты именованного канала.

Для именованного канала параметр dwCreationDisposition должен быть равен значению OPEN_EXISTING, так как клиент всегда открывает существующий именованный канал.

Для именованного канала параметр dwFlagsAndAttributes можно задается равным 0, что определяет флаги и атрибуты по умолчанию. Подробную информацию о значениях этого параметра смотри в MSDN.

Значение параметра hTemplateFile задается равным NULL.

Сделаем следующие замечания относительно работы с функцией CreateFile в случае её использования для открытия доступа к именованному каналу. Во-первых, несмотря на то, что функция WaitNamedPipe может успешно завершиться, последующий вызов функции CreateFile может завершиться неудачей по следующим причинам:

между вызовами этих функций сервер закрыл канал,

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

Для предотвращения последней ситуации сервер должен создавать новый экземпляр именованного канала после каждого успешного завершения функции ConnectNamedPipe или создать сразу несколько экземпляров именованного канала. Во-вторых, если заранее известно, что сервер вызвал функцию ConnectNamedPipe, то функция CreateFile может вызываться без предварительного вызова функции WaitNamedPipe.

Кроме того следует отметить, что если клиент работает на той же машине, что и сервер и использует для открытия именованного канала в функции CreateFile имя сервера в виде:

\\.\pipe\<pipe_name>

то файловая система именованных каналов (NPFS) открывает этот именованный канал в режиме передачи данных потоком. Чтобы открыть именованный канал в режиме передачи данных сообщениями, нужно задавать имя сервера в виде:

\\<server_name>\pipe\<pipe_name>

Отметим один момент, который касается связи сервера с клиентом именованного канала. Может возникнуть такая ситуация, что сервер вызвал функцию ConnectNamedPipe, а клиента, который хочет связаться с именованным каналом, не существует. В этом случае серверное приложение будет заблокировано. Чтобы иметь возможность обработать такую ситуацию, функцию ConnectNamedPipe следует вызывать в отдельном потоке серверного приложения. Тогда для разблокировки серверного приложения можно вызвать функцию для связи клиента с именованным каналом из другого потока.

 

4. Получение информации об именованном канале

Для получения информации о режимах работы и состоянии именованного канала используются функции:

GetNamedPipeHandleState;

GetNamedPipeInfo;

 

5. Изменение состояния именованного канала

Изменить состояние именованного канала можно посредством функции SetNamedPipeHandleState;

 

6. Обмен данными по именованному каналу

Как и в случае с анонимным каналом, для обмена данными по именованному каналу используются функции ReadFile и WriteFile, но с одним отличием, которое заключается в следующем. Так как в случае именованного канала разрешен асинхронный обмен данными, то в функциях ReadFile и WriteFile может использоваться параметр lpOverlapped при условии, что в вызове функции CreateNamedPipe в параметре dwOpenMode был установлен флаг FILE_FLAG_OVERLAPPED.

Для асинхронного ввода-вывода по именованному каналу могут также использоваться функции ReadFileEx и WriteFileEx, которые будут рассмотрены далее в одной из глав.

Для копирования данных из именованного канала используется функция PeekNamedPipe, которая копирует данные в буфер, не удаляя их из канала. Эта функция имеет следующий прототип:

PeekNamedPipe

Для обмена сообщениями по сети может также использоваться функция TransactNamedPipe, которая объединяет операции записи и чтения в одну операцию (транзакцию) и имеет следующий прототип:

T ransactNamedPipe

Параметры этой функции аналогичны параметрам функций ReadFile и WriteFile. Отметим, что функция TransactNamedPipe может использоваться только в том случае, если сервер при создании именованного канала установил флаги PIPE_TYPE_MESSAGE и PIPE_READMODE_MESSAGE.

Для передачи единственной транзакции по именованному каналу используется функция

CallNamedPipe.

После завершения обмена данными по именованному каналу, потоки должны закрыть дескрипторы экземпляров именованного канала, используя функцию CloseHandle.

 

7. Примеры работы с именованными каналами

Вначале рассмотрим простой пример, в котором процесс-сервер создает именованный канал, а затем ждет, пока клиент не соединится с именованным каналом. После этого сервер читает из именованного канала десять чисел и выводит их на консоль. Сначала приведем программу процесса-сервера именованного канала.

Листинг 1. Пример процесса сервера именованного канала.

#include <windows.h>

#include <iostream.h>

int main()

{

char c; // служебный символ

HANDLE hNamedPipe;

// создаем именованный канал для чтения hNamedPipe=CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_INBOUND, // читаем из канала

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений 1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго (LPSECURITY_ATTRIBUTES)NULL // защита по умолчанию

);

// проверяем на успешное создание if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Creation of the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl; cout << "Press any char to finish server: "; cin >> c; return 0;

}

// ждем пока клиент свяжется с каналом cout << "The server is waiting for connection with a client." << endl; if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

(LPOVERLAPPED)NULL // связь синхронная

))

{

cerr << "The connection failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any char to finish the server: ";

cin >> c;

return 0;

}

// читаем данные из канала for (int i=0; i<10; i++)

{

int nData;

DWORD dwBytesRead; if (!ReadFile(

hNamedPipe, // дескриптор канала

&nData, // адрес буфера для ввода данных

sizeof(nData), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

(LPOVERLAPPED)NULL // передача данных синхронная

))

{

cerr << "Data reading from the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any char to finish the server: ";

cin >> c;

return 0;

}

// выводим прочитанные данные на консоль cout << "The number " << nData << " was read by the server" << endl;

}

// закрываем дескриптор канала CloseHandle(hNamedPipe);

// завершаем процесс

cout << "The data are read by the server."<<endl; cout << "Press any char to finish the server: "; cin >> c; return 0;

}

 

Теперь приведем пример клиента именованного канала, который сначала связывается с именованным каналом, а затем записывает в него десять чисел.

 

Листинг 2. Пример процесса клиента именованного канала.

#include <windows.h>

#include <iostream.h>

int main()

{

char c; // служебный символ

HANDLE hNamedPipe;

char pipeName[] = "\\\\.\\pipe\\demo_pipe";

// связываемся с именованным каналом hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_WRITE, // записываем в канал

FILE_SHARE_READ, // разрешаем только запись в канал

(LPSECURITY_ATTRIBUTES) NULL, // защита по умолчанию OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

(HANDLE)NULL // дополнительных атрибутов нет

);

// проверяем связь с каналом if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl; cout << "Press any char to finish the client: "; cin >> c; return 0;

}

// пишем в именованный канал for (int i=0; i<10; i++)

{

DWORD dwBytesWritten; if (!WriteFile(

hNamedPipe, // дескриптор канала

&i, // данные

sizeof(i), // размер данных

&dwBytes Written, // количество записанных байтов

(LPOVERLAPPED)NULL // синхронная запись

))

{

// ошибка записи

cerr << "Writing to the named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl; cout << "Press any char to finish the client: "; cin >> c;

CloseHandle(hNamedPipe); return 0;

}

// выводим число на консоль

cout << "The number " << i << " is written to the named pipe." << endl;

Sleep(1000);

}

// закрываем дескриптор канала CloseHandle(hNamedPipe);

// завершаем процесс

cout << "The data are written by the client." << endl << "Press any char to finish the client: ";

cin >> c; return 0;

}

Теперь рассмотрим пример сервера именованного канала, который сначала создает именованный канал, затем ждет подключения к нему клиента. После этого сервер принимает от клиента одно сообщение, выводит это сообщение на консоль и посылает клиенту сообщение в ответ.

Листинг 3. Пример сервера именованного канала.

// Сервер принимает сообщение от клиента и посылает ему сообщение в ответ.

// Внимание: в этом случае для работы в локальной сети вход на клиентскую машину должен быть выполнен // с тем же именем и паролем, что и на сервер.

#include <windows.h>

#include <iostream.h>

int main()

{

char c; // служебный символ

HANDLE hNamedPipe;

char lpszInMessage[80]; // для сообщения от клиента

DWORD dwBytesRead; // для количества прочитанных байтов

char lpszOutMessage[] = "The server has received a message."; // обратное сообщение DWORD dwBytesWrite; // для количества записанных байтов

// создаем именованный канал для чтения hNamedPipe = CreateNamedPipe(

"V\WV\pipeV\demo_pipe", // имя канала

PIPE_ACCESS_DUPLEX, // читаем из канала и пишем в канал

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений 1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь 500 мс (LPSECURITY_ATTRIBUTES)NULL // защита по умолчанию

);

// проверяем на успешное создание if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Creation of the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl; cout << "Press any char to finish server: "; cin >> c; return 0;

}

// ждем, пока клиент свяжется с каналом cout << "The server is waiting for connection with a client." << endl; if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

(LPOVERLAPPED)NULL // связь синхронная

))

{

cerr << "The connection failed." << endl

<< "The last error code: "<<GetLastError() << endl; CloseHandle(hNamedPipe); cout << "Press any char to finish the server: "; cin >> c; return 0;

}

// читаем сообщение от клиента if (!ReadFile(

hNamedPipe, // дескриптор канала

lpszInMessage, // адрес буфера для ввода данных

sizeof(lpszInMessage), // число читаемых байтов

&dwBytesRead, // число прочитанных байтов

(LPOVERLAPPED)NULL // передача данных синхронная

))

{

cerr << "Data reading from the named pipe failed." << endl << "The last error code: "<< GetLastErrorO << endl; CloseHandle(hNamedPipe); cout << "Press any char to finish the server: "; cin >> c; return 0;

}

// выводим полученное от клиента сообщение на консоль cout << "The server has received the following message from a client: "

<< endl << "\t" << lpszInMessage << endl;

// отвечаем клиенту if (!WriteFile(

hNamedPipe, // дескриптор канала

lpszOutMessage, // адрес буфера для вывода данных

sizeof(lpszOutMessage), // число записываемых байтов

&dwBytesWrite, // число записанных байтов

(LPOVERLAPPED)NULL // передача данных синхронная

))

{

cerr << "Data writing to the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any char to finish the server: ";

cin >> c;

return 0;

}

// выводим посланное клиенту сообщение на консоль cout << "The server send the following message to a client: "

<< endl << "\t" << lpszOutMessage << endl;

// закрываем дескриптор канала CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any char to finish the server: "; cin >> c; return 0;

}

Обратим в этой программе внимание на следующий момент. Если клиент и сервер работают на разных компьютерах локальной сети, то вход как на компьютер сервера, так и на компьютер клиента, должен осуществляться с одинаковыми именами и паролями. Так как по умолчанию атрибуты защиты именованного канала устанавливаются таким образом, что он принадлежит только пользователю, создавшему этот именованный канал. В следующей программе мы установим атрибуты защиты таким образом, чтобы они разрешали доступ к именованному каналу любому пользователю.

// Пример процесса сервера именованного канала.

// Сервер принимает сообщение от клиента и посылает ему сообщение в ответ.

// В этом случае для работы в локальной сети вход на клиентскую машину может быть // выполнен с любым именем и паролем.

#include <windows.h>

#include <iostream.h>

int main()

{

char c; // служебный символ

SECURITY_ATTRIBUTES sa; // атрибуты защиты

SECURITY_DESCRIPTOR sd; // дескриптор защиты

HANDLE hNamedPipe;

char lpszInMessage[80]; // для сообщения от клиента

DWORD dwBytesRead; // для числа прочитанных байтов

char lpszOutMessage[] = "The server has received a message."; // обратное сообщение DWORD dwBytesWrite; // для числа записанных байтов

// инициализация атрибутов защиты sa.nLength = sizeof(sa);

sa.bInheritHandle = FALSE; // дескриптор канала ненаследуемый // инициализируем дескриптор защиты InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);

// устанавливаем атрибуты защиты, разрешая доступ всем пользователям SetSecurityDescriptorDacl(&sd, TRUE, NULL, fAlSE); sa.lpSecurityDescriptor = &sd;

// создаем именованный канал для чтения hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_DUPLEX, // читаем из канала и пишем в канал

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений 1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь 500 мс &sa // доступ для всех пользователей

);

// проверяем на успешное создание if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Creation of the named pipe failed." << endl

<< "The last error code: " << GetLastErrorO << endl; cout << "Press any char to finish server: "; cin >> c; return 0;

}

// ждем, пока клиент свяжется с каналом cout << "The server is waiting for connection with a client." << endl; if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

(LPOVERLAPPED)NULL // связь синхронная

))

{

cerr &l





Поделиться с друзьями:


Дата добавления: 2016-11-24; Мы поможем в написании ваших работ!; просмотров: 731 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Своим успехом я обязана тому, что никогда не оправдывалась и не принимала оправданий от других. © Флоренс Найтингейл
==> читать все изречения...

2351 - | 2156 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.014 с.