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

Вариант 2

Задание

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

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

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

2.Ожидает подключения клиента к каналу функцией ConnectNamedPipe().

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

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

Необходимо сохранить в памяти значение под указанным ключом и записать в канал строку acknowledged.

3.2.Получить значение по ключу. Формат команды: get ключ

Если ключ имеется в хранилище, следует записать в канал строку в формате found значение

В противном случает следует записать в канал строку missing. Указание. Запись можно также выполнить функцией WriteFile () .

3.3.Получить список ключей в хранилище. Формат команды:

list

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

3.4.Удалить значение под заданным ключом. Формат команды: delete ключ

Если ключ присутствует в хранилище, следует записать в канал строку deleted, иначе — строку missing.

3.5.Прекратить сеанс связи. Формат команды:

quit

Необходимо отключить именованный канал от клиента функцией DisconnectNamedPipe().

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

5.Запрашивает у пользователя, следует ли остановить сервер.

В случае утвердительного ответа следует уничтожить именованный канал, созданный

на шаге 1, функцией CloseHandle().

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

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

1.Запрашивает у пользователя имя канала и подключается как клиент к указанному каналу функцией

CreateFile().

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

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

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

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

Указание 1. Хранение значений по ключам в C++ удобно реализовать с std::map , а вычленение имени команды и аргументов из строки — классом std::stringstream .

Указание 2. При отладке первой из двух программ целесообразно использовать образец

решения второй в качестве недостающей части. Раздел MSDN об именованных каналах также содержит пример, близкий к программе-клиенту: https://docs.microsoft.com/ru-ru/windows/win32/ipc/named-pipe-client?redirectedfrom=MSDN

Проверку работы программ следует выполнять, запуская несколько экземпляров приложения, например, из «Проводника».

1

Выполнение

Сервер (server.cpp):

#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>;//Для данных ключ-значение

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);

}

//Ожидание подключения клиента 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;

}

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

void HandleConnection(HANDLE pipe, dictionary_t& dict)

{

2

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.";

while (true)

{

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

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;

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

{

DisconnectNamedPipe(pipe);

break;

}

DWORD written;

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

{

std::string key, value; stream >> key >> value; dict[key] = value;

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

}

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

{

std::string key; stream >> key;

auto it = dict.find(key);

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

{

3

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

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

}

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

{

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

}

}

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

{

std::string answer;

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

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

}

//Удаление значения под заданным ключом 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);

}

}

}

//Запрос у пользователя, следует ли остановить сервер 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";

4

}

}

int main()

{

auto pipe = StartServer();

if (pipe == INVALID_HANDLE_VALUE)

{

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

}

dictionary_t data; while (true)

{

if (!fconnected(pipe)) return EXIT_FAILURE;

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

break;

}

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

}

Client.cpp:

#include <windows.h> #include <iostream> #include <sstream> #include <string>

#define MAX_BUFFER_SIZE 64

int main()

{

//Запрос имени канала 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);

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)

5

{

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

}

std::string buf; DWORD written, read; while (true)

{

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

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

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

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

break;

char msg_buffer[MAX_BUFFER_SIZE];

ReadFile(pipe, msg_buffer, MAX_BUFFER_SIZE, &read, nullptr);//Считывание ответа сервера msg_buffer[read] = 0;

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

}

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

}

6

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