Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
6
Добавлен:
31.01.2022
Размер:
84.9 Кб
Скачать

Отчет по лабораторной работе № 2 по дисциплине «Системное программное обеспечение»

на тему: «Межпроцессорное взаимодействие»

Вариант 2

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

Программа-сервер:

Объявления и включения в начале программы: #include <windows.h>

#include <iostream> #include <sstream> #include <string> #include <map>

#define MAX_BUFFER_SIZE 64

using dictionary_t = std::map<std::string, std::string>;//Для данных ключ-значение

1. Запрашиваем у пользователя имя канала и создаем дуплексный, ориентированный на сообщения канал с заданным именем функцией CreateNamedPipe().

HANDLE StartServer()

{

std::string pipe_name; //Запрос имени канала

std::cout << "Enter pipe name: "; std::getline(std::cin, pipe_name);

auto path = "\\\\.\\pipe\\" + pipe_name;

//Создание дуплексного, ориентированного на сообщения канала return CreateNamedPipe(

path.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES, MAX_BUFFER_SIZE, MAX_BUFFER_SIZE,

0,

nullptr);

}

2.Ожидаем подключения клиента к каналу функцией ConnectNamedPipe().Если успешного подключения не произошло, выводим код ошибки и закрываем канал. Иначе выводим сообщение о соединении.

//Ожидание подключения клиента bool fconnected(HANDLE pipe)

{

std::cout << "Waiting for client... ";

if (!ConnectNamedPipe(pipe, nullptr))

{

auto err = GetLastError();

if (err != ERROR_PIPE_CONNECTED)

{

std::cout << "Error. Error code: " << err <<'\n'; CloseHandle(pipe);

return false;

}

}

std::cout << "connected.\n"; return true;

}

3.Считываем из подключенного канала одну строку-команду, состоящую из имени и аргументов, разделенных пробелами (ни в имени, ни в аргументах пробелов нет) функцией ReadFile(). Если клиент разорвал соединение или произошла другая ошибка чтения, выводим соответствующее сообщение. Код для всех подпунктов 3 содержится в функции

void HandleConnection(HANDLE pipe, dictionary_t& dict). std::cout << "Waiting for command... ";

2

char msg_buffer[MAX_BUFFER_SIZE]; DWORD read = 0;

//Чтение запроса клиента

auto fsuccess = ReadFile(pipe, msg_buffer, MAX_BUFFER_SIZE, &read, nullptr); if (!fsuccess)

{

auto err = GetLastError();

if (err == ERROR_BROKEN_PIPE)//Если клиент разорвал соединение std::cout << "client disconnected.\n";

else

std::cout << "Error. Error code: " << err <<'\n'; DisconnectNamedPipe(pipe);

break;

}

msg_buffer[read] = 0;

std::cout << "received: '" << msg_buffer << "'.\n"; std::stringstream stream{msg_buffer};

std::string cmd; stream >> cmd;

Объявляем константы, которые будем использовать в пунктах 3.1-3.5: std::string ack = "acknowledged";

std::string del = "deleted"; std::string found = "found "; std::string miss = "missing";

std::string wrong_cmd = "Wrong command! Commands: get, set, list, delete or quit.";

3.1. Выполняем сохранение значения по ключу. Формат команды: set ключ значение. Сохраняем в памяти значение под указанным ключом и записываем в канал строку acknowledged.

DWORD written;

//Сохранение значения по ключу if (cmd == "set")

{

std::string key, value;

3

stream >> key >> value; dict[key] = value;

WriteFile(pipe, ack.c_str(), ack.size(), &written, nullptr);

}

3.2.Получаем значение по ключу. Формат команды: get ключ. Если ключ имеется в хранилище, записываем в канал строку в формате: found значение. В противном случае записываем в канал строку missing.

//Получение значения по ключу else if (cmd == "get")

{

std::string key; stream >> key;

auto it = dict.find(key);

if (it != dict.end())//Если ключ есть в хранилище

{

auto answer = "found " + it->second;

WriteFile(pipe, answer.c_str(), answer.size(), &written, nullptr);

}

else//Если ключа нет в хранилище

{

WriteFile(pipe, miss.c_str(), miss.size(), &written, nullptr);

}

}

3.3.Получаем список ключей в хранилище. Формат команды: list. Записываем в канал строку, содержащую через пробел все имеющиеся в хранилище ключи.

//Получение списка ключей в хранилище else if (cmd == "list")

{

std::string answer;

for (const auto& kv : dict) answer += kv.first + ' ';

WriteFile(pipe, answer.c_str(), answer.size(), &written, nullptr);

4

}

3.4. Удаляем значение под заданным ключом. Формат команды: delete ключ. Если ключ присутствует в хранилище, записываем в канал строку deleted, иначе — строку missing.

//Удаление значения под заданным ключом else if (cmd == "delete")

{

std::string key; stream >> key;

auto it = dict.find(key);

if (it != dict.end())//Если ключ есть в хранилище

{

dict.erase(it);

WriteFile(pipe, del.c_str(), del.size(), &written, nullptr);

}

else//Если ключа нет в хранилище

{

WriteFile(pipe, miss.c_str(), miss.size(), &written, nullptr);

}

}

Если пользователь ввел неверную команду, указываем на ошибку. //Если неверная команда

else

{

WriteFile(pipe, wrong_cmd.c_str(), wrong_cmd.size(), &written, nullptr);

}

3.5. Прекращаем сеанс связи. Формат команды: quit. Отключаем именованный канал от клиента функцией DisconnectNamedPipe().

//Прекращение сеанса связи if (cmd == "quit")

{

DisconnectNamedPipe(pipe);

5

break;

}

4. Переходим к пункту 3 (цикл работы с подключенным клиентом).

Пункты 3.1-3.5 выполняются в бесконечном цикле, который прерывается, когда пользователь вводит команду quit.

5.Запрашиваем у пользователя, следует ли остановить сервер. В случае утвердительного ответа уничтожаем именованный канал, созданный на шаге 1, функцией СloseHandle().

//Запрос у пользователя, следует ли остановить сервер bool StopServer()

{

while (true)

{

std::cout << "Stop server [y/n]? "; int curr = std::cin.get();

if (curr == 'y') return true; if (curr == 'n') return false;

std::cout << "Wrong input. Please enter 'y' for 'yes' or 'n' for 'no'.\n";

}

}

6.Переходим к пункту 2 (цикл ожидания клиентов и работы с ними).

Переход осуществляется в главной программе при помощи бесконечного цикла, который прерывается, только если пользователь ответил утвердительно на вопрос об остановке сервера. int main()

{

auto pipe = StartServer();

if (pipe == INVALID_HANDLE_VALUE)

{

std::cout << "Error. Error code: " << GetLastError()<<'\n'; return EXIT_FAILURE;

6

}

dictionary_t data; while (true)

{

if (!fconnected(pipe)) return EXIT_FAILURE;

HandleConnection(pipe, data); if (StopServer())

break;

}

CloseHandle(pipe);//Закрытие канала return EXIT_SUCCESS;

}

Программа-клиент:

Объявления и включения в начале программы: #include <windows.h>

#include <iostream> #include <sstream> #include <string>

#define MAX_BUFFER_SIZE 64

Все действия выполняются в главной программе.

1. Запрашиваем у пользователя имя канала и подключаемся как клиент к указанному каналу функцией CreateFile(). Если не удалось подключиться, закрываем канал, выводим код ошибки и завершаем программу.

//Запрос имени канала std::string pipe_name;

std::cout << "Enter pipe name: "; std::getline(std::cin, pipe_name);

auto pipe_path = "\\\\.\\pipe\\" + pipe_name; //Подключение к указанному каналу

auto pipe = CreateFile(pipe_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);

7

if (pipe != INVALID_HANDLE_VALUE)

{

DWORD dwmode = PIPE_READMODE_MESSAGE | PIPE_WAIT;

auto fsuccess = SetNamedPipeHandleState(pipe, &dwmode, nullptr, nullptr); if (!fsuccess)

{

CloseHandle(pipe);

pipe = INVALID_HANDLE_VALUE;

}

}

if (pipe == INVALID_HANDLE_VALUE)

{

std::cout << "Error. Error code: " << GetLastError()<<'\n'; return EXIT_FAILURE;

}

2.Запрашиваем у пользователя строку-команду и записываем её в открытый канал функцией WriteFile().

std::string buf; DWORD written;

//Запрос у пользователя строки-команды std::cout << "> ";

std::getline(std::cin, buf);

WriteFile(pipe, buf.c_str(), buf.size(), &written, nullptr);//Запись строки в канал

3.Если на шаге 2 была введена команда quit, закрываем канал функцией CloseHandle() и завершаем работу.

//Переход к закрытию канала if (buf == "quit")

break;

После бесконечного цикла, выход из которого производится по команде quit, следует закрытие канала:

CloseHandle(pipe);//Закрытие канала

8

4.Считываем из канала ответ функцией ReadFile() и отображаем его на экране. DWORD read;

char msg_buffer[MAX_BUFFER_SIZE]; //Считывание ответа сервера

ReadFile(pipe, msg_buffer, MAX_BUFFER_SIZE, &read, nullptr); msg_buffer[read] = 0;

std::cout << msg_buffer << '\n';//Отображение ответа сервера

5.Переходим к шагу 2 (цикл).

Вбесконечный цикл входят команды пунктов 2-4. Выход из цикла производится по команде quit.

Пример работы программ

9

Соседние файлы в папке Windows лаб 1-4