From ff6e412049ad8a5f6b1659d98bc7a90504fbf6d7 Mon Sep 17 00:00:00 2001 From: pajjilykk Date: Thu, 11 Dec 2025 08:08:53 +0700 Subject: [PATCH] 5-vlad --- vlad/lab_5/Makefile | 79 +++++------ vlad/lab_5/client.c | 160 ++++++++++++++++++++++ vlad/lab_5/common.h | 16 --- vlad/lab_5/server.c | 327 +++++++++++++++++--------------------------- vlad/lab_5/worker.c | 123 ----------------- 5 files changed, 327 insertions(+), 378 deletions(-) create mode 100644 vlad/lab_5/client.c delete mode 100644 vlad/lab_5/common.h delete mode 100644 vlad/lab_5/worker.c diff --git a/vlad/lab_5/Makefile b/vlad/lab_5/Makefile index 7e9a8b3..8f65c9e 100644 --- a/vlad/lab_5/Makefile +++ b/vlad/lab_5/Makefile @@ -1,60 +1,61 @@ -CC = gcc -CFLAGS = -Wall -Wextra -std=c99 -g -LDFLAGS_MQ = -lrt +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 -g +LDFLAGS_MQ = -lrt # POSIX message queues on Linux -# SERVER_ARGS: -# WORKER_ARGS: -SERVER_ARGS = 1000 15 500 500 -WORKER_ARGS = 7 +TEST_INPUT = test_input.txt +TEST_OUTPUT = test_output.txt all: msg # ===== POSIX MQ targets ===== -msg: msg_server msg_worker +msg: mq_server mq_client -msg_server: server.c common.h +mq_server: server.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_MQ) -msg_worker: worker.c common.h +mq_client: client.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS_MQ) -# ===== Tests ===== -test_msg_server: msg - @echo "=== Запуск сервера POSIX MQ ===" - @echo "В другом терминале выполните: make test_msg_workers" - ./msg_server $(SERVER_ARGS) +# ===== Ручные тесты ===== -test_msg_workers: msg - @echo "=== Запуск нескольких пчёл ===" - ./msg_worker 7 & \ - ./msg_worker 9 & \ - ./msg_worker 5 & \ - wait +test_server: msg + @echo "=== Запуск MQ сервера ===" + @echo "В другом терминале выполните: make test_client_manual" + ./mq_server + +test_client_manual: msg + @echo "=== Запуск MQ клиента (ручной тест) ===" + ./mq_client $(TEST_INPUT) $(TEST_OUTPUT) + +# ===== Автотест: сервер в фоне + клиент ===== -# Автотест: сервер в фоне, несколько пчёл test_all: msg - @echo "=== Автотест POSIX MQ ===" - ./msg_server $(SERVER_ARGS) & \ + @echo "=== Автотест MQ (server + client) ===" + @echo "Создание тестового входного файла..." + echo "aabbccddeeff" > $(TEST_INPUT) + @echo "Старт сервера в фоне..." + ./mq_server & \ SRV=$$!; \ - sleep 2; \ - ./msg_worker $(WORKER_ARGS) & \ - ./msg_worker $(WORKER_ARGS) & \ - ./msg_worker $(WORKER_ARGS) & \ - wait; \ - wait $$SRV + sleep 1; \ + echo "Запуск клиента..."; \ + ./mq_client $(TEST_INPUT) $(TEST_OUTPUT); \ + echo "Остановка сервера..."; \ + kill $$SRV || true; \ + wait $$SRV 2>/dev/null || true; \ + echo "=== Содержимое $(TEST_OUTPUT) ==="; \ + cat $(TEST_OUTPUT) -# Очистка clean: @echo "Очистка..." - rm -f msg_server msg_worker + rm -f mq_server mq_client *.o $(TEST_INPUT) $(TEST_OUTPUT) help: @echo "Available targets:" - @echo " msg - Build POSIX message queue programs" - @echo " test_msg_server - Run MQ server (use workers in another terminal)" - @echo " test_msg_workers - Run several worker processes" - @echo " test_all - Automatic end-to-end test" - @echo " clean - Remove built files" - @echo " help - Show this help" + @echo " msg - Build POSIX MQ programs" + @echo " test_server - Run MQ server (client separately)" + @echo " test_client_manual- Run client (server must be running)" + @echo " test_all - Automatic end-to-end test (server+client)" + @echo " clean - Remove built and test files" + @echo " help - Show this help" -.PHONY: all msg test_msg_server test_msg_workers test_all clean help +.PHONY: all msg test_server test_client_manual test_all clean help diff --git a/vlad/lab_5/client.c b/vlad/lab_5/client.c new file mode 100644 index 0000000..b84c2d0 --- /dev/null +++ b/vlad/lab_5/client.c @@ -0,0 +1,160 @@ +// client.c (mq_client.c) +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MQ_REQUEST "/mq_request" +#define MQ_RESPONSE "/mq_response" +#define MQ_MAXMSG 10 +#define BUFFER_SIZE 4096 + +void print_usage(const char *progname) { + fprintf(stderr, "Usage: %s \n", progname); + fprintf(stderr, "Example: %s input.txt output.txt\n", progname); +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "ERROR: Неверное количество аргументов\n"); + print_usage(argv[0]); + return 1; + } + + const char *input_file = argv[1]; + const char *output_file = argv[2]; + + printf("=== MQ Client ===\n"); + printf("Input file : %s\n", input_file); + printf("Output file: %s\n", output_file); + + int in_fd = open(input_file, O_RDONLY); + if (in_fd < 0) { + fprintf(stderr, "ERROR: Не удалось открыть входной файл %s: %s\n", + input_file, strerror(errno)); + return 1; + } + + char *buffer = malloc(BUFFER_SIZE); + if (!buffer) { + fprintf(stderr, "ERROR: Не удалось выделить память\n"); + close(in_fd); + return 1; + } + + ssize_t bytes_read = read(in_fd, buffer, BUFFER_SIZE - 1); + close(in_fd); + if (bytes_read < 0) { + fprintf(stderr, "ERROR: Не удалось прочитать файл: %s\n", + strerror(errno)); + free(buffer); + return 1; + } + + buffer[bytes_read] = '\0'; + printf("Прочитано байт из файла: %zd\n", bytes_read); + + struct mq_attr attr; + memset(&attr, 0, sizeof(attr)); + attr.mq_flags = 0; + attr.mq_maxmsg = MQ_MAXMSG; + attr.mq_msgsize = BUFFER_SIZE; + attr.mq_curmsgs = 0; + + mqd_t mq_req = mq_open(MQ_REQUEST, O_WRONLY); + if (mq_req == (mqd_t)-1) { + fprintf(stderr, + "ERROR: Не удалось открыть очередь запросов %s: %s\n", + MQ_REQUEST, strerror(errno)); + fprintf(stderr, "Убедитесь, что сервер запущен!\n"); + free(buffer); + return 1; + } + + mqd_t mq_resp = mq_open(MQ_RESPONSE, O_RDONLY); + if (mq_resp == (mqd_t)-1) { + fprintf(stderr, + "ERROR: Не удалось открыть очередь ответов %s: %s\n", + MQ_RESPONSE, strerror(errno)); + mq_close(mq_req); + free(buffer); + return 1; + } + + if (mq_send(mq_req, buffer, (size_t)bytes_read, 0) == -1) { + fprintf(stderr, "ERROR: mq_send failed: %s\n", strerror(errno)); + mq_close(mq_req); + mq_close(mq_resp); + free(buffer); + return 1; + } + + printf("Отправлено байт: %zd\n", bytes_read); + + // ВАЖНО: размер буфера для mq_receive >= mq_msgsize + ssize_t resp_bytes = mq_receive(mq_resp, buffer, BUFFER_SIZE, NULL); + if (resp_bytes < 0) { + fprintf(stderr, "ERROR: mq_receive failed: %s\n", strerror(errno)); + mq_close(mq_req); + mq_close(mq_resp); + free(buffer); + return 1; + } + + if (resp_bytes >= BUFFER_SIZE) + resp_bytes = BUFFER_SIZE - 1; + buffer[resp_bytes] = '\0'; + + printf("Получено байт от сервера: %zd\n", resp_bytes); + + char *repl_info = strstr(buffer, "\nREPLACEMENTS:"); + long long replacements = 0; + + if (repl_info) { + sscanf(repl_info, "\nREPLACEMENTS:%lld", &replacements); + *repl_info = '\0'; + resp_bytes = repl_info - buffer; + } else { + fprintf(stderr, + "WARNING: Не найдена служебная строка REPLACEMENTS, " + "запишем весь ответ как есть\n"); + } + + int out_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH); + if (out_fd < 0) { + fprintf(stderr, "ERROR: Не удалось открыть выходной файл %s: %s\n", + output_file, strerror(errno)); + mq_close(mq_req); + mq_close(mq_resp); + free(buffer); + return 1; + } + + ssize_t written = write(out_fd, buffer, resp_bytes); + close(out_fd); + if (written != resp_bytes) { + fprintf(stderr, "ERROR: Ошибка записи в выходной файл\n"); + mq_close(mq_req); + mq_close(mq_resp); + free(buffer); + return 1; + } + + printf("Записано байт в выходной файл: %zd\n", written); + printf("Количество выполненных замен: %lld\n", replacements); + printf("\nОбработка завершена успешно!\n"); + + mq_close(mq_req); + mq_close(mq_resp); + free(buffer); + return 0; +} diff --git a/vlad/lab_5/common.h b/vlad/lab_5/common.h deleted file mode 100644 index 2399e11..0000000 --- a/vlad/lab_5/common.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -#define REQ_QUEUE "/winnie_req" -#define NAME_MAXLEN 64 - -typedef struct { - pid_t pid; - int want; - char replyq[NAME_MAXLEN]; -} req_msg_t; - -typedef struct { - int granted; - int remain; -} rep_msg_t; diff --git a/vlad/lab_5/server.c b/vlad/lab_5/server.c index 9559569..ccf19ee 100644 --- a/vlad/lab_5/server.c +++ b/vlad/lab_5/server.c @@ -1,230 +1,157 @@ -#define _GNU_SOURCE // для определения расширенных возможностей glibc +// mq_server.c +#include +#include +#include +#include +#include +#include +#include #include -#include // POSIX очереди сообщений: mq_open, mq_send, mq_receive, mq_timedreceive -#include // sig_atomic_t, signal, SIGINT -#include // bool, true/false -#include -#include // printf, fprintf, perror -#include // atoi, malloc/free при желании -#include // memset, strncpy, strncmp -#include // константы прав доступа к объектам ФС -#include // clock_gettime, nanosleep, struct timespec +#include -#include "common.h" // описания REQ_QUEUE, NAME_MAXLEN, req_msg_t, rep_msg_t и т.п. +#include // POSIX message queues -#ifndef MAX_CLIENTS -#define MAX_CLIENTS 128 // максимальное число клиентов, чьи очереди мы запомним -#endif +#define MQ_REQUEST "/mq_request" +#define MQ_RESPONSE "/mq_response" +#define MQ_MAXMSG 10 +#define BUFFER_SIZE 4096 // msgsize очереди и размер буферов -// Глобальный флаг остановки сервера по сигналу -static volatile sig_atomic_t stop_flag = 0; -// Обработчик SIGINT: просто выставляет флаг -static void on_sigint(int) { stop_flag = 1; } +volatile sig_atomic_t running = 1; -// Удобная функция "уснуть" на ms миллисекунд -static void msleep(int ms) { - struct timespec ts = { - .tv_sec = ms / 1000, - .tv_nsec = (ms % 1000) * 1000000L - }; - nanosleep(&ts, NULL); +void signal_handler(int sig) { + (void)sig; + running = 0; } -/* Простейший реестр клиентов: массив имён их ответных очередей */ -static char clients[MAX_CLIENTS][NAME_MAXLEN]; -static int client_count = 0; +// во всех парах одинаковых подряд символов второй -> пробел +long long process_text(const char *input, size_t len, + char *output, size_t out_size) { + if (out_size == 0) return -1; -// Добавляет имя очереди клиента в список, если его там ещё нет -static void add_client(const char *name) { - if (!name || name[0] == '\0') return; - // Проверка на дубликат - for (int i = 0; i < client_count; ++i) { - if (strncmp(clients[i], name, NAME_MAXLEN) == 0) return; - } - // Если есть место — копируем имя в массив - if (client_count < MAX_CLIENTS) { - strncpy(clients[client_count], name, NAME_MAXLEN - 1); - clients[client_count][NAME_MAXLEN - 1] = '\0'; - client_count++; - } -} + long long replacements = 0; + size_t out_pos = 0; -/* Рассылает всем запомненным клиентам сообщение STOP (granted=0, remain=0) */ -static void send_stop_to_clients(void) { - rep_msg_t stoprep; - memset(&stoprep, 0, sizeof(stoprep)); - stoprep.granted = 0; - stoprep.remain = 0; + for (size_t i = 0; i < len && out_pos < out_size - 1; i++) { + char c = input[i]; + output[out_pos++] = c; - for (int i = 0; i < client_count; ++i) { - // Открываем очередь ответа клиента только на запись - mqd_t q = mq_open(clients[i], O_WRONLY); - if (q == -1) { - // Лучшая попытка: если открыть не удалось — просто пропускаем - continue; + if (i + 1 < len && input[i] == input[i + 1]) { + // вторая буква пары + if (out_pos < out_size - 1) { + output[out_pos++] = ' '; + replacements++; + } + i++; // пропускаем второй символ исходного текста } - // Отправляем структуру-ответ без приоритета (0) - mq_send(q, (const char *) &stoprep, sizeof(stoprep), 0); - mq_close(q); } + + output[out_pos] = '\0'; + return replacements; } -int main(int argc, char **argv) { - // Ожидается: total_honey, portion, period_ms, starvation_ms - if (argc != 5) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 2; - } - - // Разбираем параметры симуляции - int remain = atoi(argv[1]); // сколько "мёда" всего - int portion = atoi(argv[2]); // сколько Винни ест за один подход - int period_ms = atoi(argv[3]); // период "еды" в миллисекундах - int starvation_ms = atoi(argv[4]); // через сколько мс без еды считаем, что Винни умер с голоду - - if (remain < 0 || portion <= 0 || period_ms <= 0 || starvation_ms < 0) { - fprintf(stderr, "Bad args\n"); - return 2; - } - - // На всякий случай удаляем старую очередь запросов, если осталась - mq_unlink(REQ_QUEUE); - - // Настроиваем атрибуты очереди запросов +int main(void) { struct mq_attr attr; - memset(&attr, 0, sizeof(attr)); - attr.mq_maxmsg = 10; // максимум 10 сообщений в очереди - attr.mq_msgsize = sizeof(req_msg_t); // размер одного сообщения = размер структуры запроса + mqd_t mq_req = (mqd_t)-1; + mqd_t mq_resp = (mqd_t)-1; - // Открываем общую очередь запросов: создаём и даём читать/писать - // (читаем заявки и также сможем через неё послать STOP при желании) - mqd_t qreq = mq_open(REQ_QUEUE, O_CREAT | O_RDWR, 0666, &attr); - if (qreq == (mqd_t) -1) { - perror("mq_open qreq"); - fprintf(stderr, "Hint: ensure msg_max>=10 and msgsize_max>=%zu\n", sizeof(req_msg_t)); + memset(&attr, 0, sizeof(attr)); + attr.mq_flags = 0; + attr.mq_maxmsg = MQ_MAXMSG; + attr.mq_msgsize = BUFFER_SIZE; + attr.mq_curmsgs = 0; + + mq_unlink(MQ_REQUEST); + mq_unlink(MQ_RESPONSE); + + mq_req = mq_open(MQ_REQUEST, O_CREAT | O_RDONLY, 0666, &attr); + if (mq_req == (mqd_t)-1) { + fprintf(stderr, "ERROR: mq_open(%s) failed: %s\n", + MQ_REQUEST, strerror(errno)); return 1; } - // Для отладки выводим реальные атрибуты очереди - struct mq_attr got; - if (mq_getattr(qreq, &got) == 0) { - fprintf(stderr, "Server: q=%s maxmsg=%ld msgsize=%ld cur=%ld\n", - REQ_QUEUE, got.mq_maxmsg, got.mq_msgsize, got.mq_curmsgs); + mq_resp = mq_open(MQ_RESPONSE, O_CREAT | O_WRONLY, 0666, &attr); + if (mq_resp == (mqd_t)-1) { + fprintf(stderr, "ERROR: mq_open(%s) failed: %s\n", + MQ_RESPONSE, strerror(errno)); + mq_close(mq_req); + mq_unlink(MQ_REQUEST); + return 1; } - // Обработчик Ctrl+C: аккуратное завершение - signal(SIGINT, on_sigint); + printf("=== MQ Server started ===\n"); + printf("Request queue : %s\n", MQ_REQUEST); + printf("Response queue: %s\n", MQ_RESPONSE); - fprintf(stderr, "Server: started remain=%d portion=%d period=%dms starve=%dms\n", - remain, portion, period_ms, starvation_ms); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); - // Инициализируем "текущее время" и моменты следующего приёма пищи / последней еды - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - long long now_ns = (long long) now.tv_sec * 1000000000LL + now.tv_nsec; - long long next_eat_ns = now_ns + (long long) period_ms * 1000000LL; // когда Винни в следующий раз ест - long long last_feed_ns = now_ns; // когда он ел в последний раз + char in_buf[BUFFER_SIZE]; + char out_buf[BUFFER_SIZE]; - req_msg_t req; // буфер для входящего запроса - bool need_stop_broadcast = false; // флаг: нужно ли разослать клиентам STOP - - // Главный цикл сервера: обрабатываем запросы и "еду" до сигнала или окончания мёда - while (!stop_flag) { - // Обновляем текущее время - clock_gettime(CLOCK_MONOTONIC, &now); - now_ns = (long long) now.tv_sec * 1000000000LL + now.tv_nsec; - - // Сколько мс до следующего приёма пищи - int sleep_ms = (int) ((next_eat_ns - now_ns) / 1000000LL); - if (sleep_ms < 0) sleep_ms = 0; - - // Дедлайн для mq_timedreceive: ждём сообщение не дольше sleep_ms - struct timespec deadline = { - .tv_sec = now.tv_sec + sleep_ms / 1000, - .tv_nsec = now.tv_nsec + (sleep_ms % 1000) * 1000000L - }; - if (deadline.tv_nsec >= 1000000000L) { - deadline.tv_sec++; - deadline.tv_nsec -= 1000000000L; - } - - // Пытаемся принять запрос до наступления дедлайна - ssize_t rd = mq_timedreceive(qreq, (char *) &req, sizeof(req), NULL, &deadline); - if (rd >= 0) { - // Успешно прочитали структуру запроса - if (req.want != 0) { - // Регистрируем очередь ответа клиента, чтобы уметь послать ему STOP - add_client(req.replyq); - - rep_msg_t rep; - if (remain > 0) { - int grant = req.want; - if (grant > remain) grant = remain; - remain -= grant; - rep.granted = grant; // сколько реально дали - rep.remain = remain; // сколько осталось - } else { - // Мёд закончился — ничего не даём - rep.granted = 0; - rep.remain = 0; - } - - // Открываем очередь ответа клиента и отправляем ему ответ - mqd_t qrep = mq_open(req.replyq, O_WRONLY); - if (qrep != (mqd_t) -1) { - mq_send(qrep, (const char *) &rep, sizeof(rep), 0); - mq_close(qrep); - } - } - } else if (errno != ETIMEDOUT && errno != EAGAIN) { - // Любая ошибка, кроме "таймаут" или "временно нет сообщений", логируется - perror("mq_timedreceive"); - } - - // После приёма (или таймаута) обновляем текущее время и проверяем, пора ли Винни есть - clock_gettime(CLOCK_MONOTONIC, &now); - now_ns = (long long) now.tv_sec * 1000000000LL + now.tv_nsec; - - if (now_ns >= next_eat_ns) { - if (remain > 0) { - // Винни ест свою порцию (или остаток, если мёда меньше порции) - int eat = portion; - if (eat > remain) eat = remain; - remain -= eat; - last_feed_ns = now_ns; - fprintf(stderr, "Winnie eats %d, remain=%d\n", eat, remain); - } else { - // Мёда нет: проверяем, не умер ли Винни с голоду (starvation_ms) - if (starvation_ms > 0 && - (now_ns - last_feed_ns) / 1000000LL >= starvation_ms) { - fprintf(stderr, "Winnie starved, stopping\n"); - need_stop_broadcast = true; - break; - } - } - // Планируем следующий приём пищи через period_ms - next_eat_ns = now_ns + (long long) period_ms * 1000000LL; - } - - // Если мёд закончился, надо будет всем сообщить STOP и завершаться - if (remain <= 0) { - need_stop_broadcast = true; + while (running) { + unsigned int prio = 0; + ssize_t bytes_read = mq_receive(mq_req, in_buf, + sizeof(in_buf), &prio); + if (bytes_read < 0) { + if (errno == EINTR && !running) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "ERROR: mq_receive failed: %s\n", + strerror(errno)); break; } + + if (bytes_read >= (ssize_t)sizeof(in_buf)) + bytes_read = sizeof(in_buf) - 1; + in_buf[bytes_read] = '\0'; + printf("Received request: %zd bytes\n", bytes_read); + + long long repl = process_text(in_buf, (size_t)bytes_read, + out_buf, sizeof(out_buf)); + if (repl < 0) { + const char *err_msg = "ERROR: processing failed\n"; + mq_send(mq_resp, err_msg, strlen(err_msg), 0); + continue; + } + + printf("Replacements done: %lld\n", repl); + + char resp_buf[BUFFER_SIZE]; + size_t processed_len = strlen(out_buf); + + if (processed_len + 64 >= sizeof(resp_buf)) { + const char *err_msg = "ERROR: response too long\n"; + mq_send(mq_resp, err_msg, strlen(err_msg), 0); + continue; + } + + memcpy(resp_buf, out_buf, processed_len); + int n = snprintf(resp_buf + processed_len, + sizeof(resp_buf) - processed_len, + "\nREPLACEMENTS:%lld\n", repl); + if (n < 0) { + const char *err_msg = "ERROR: snprintf failed\n"; + mq_send(mq_resp, err_msg, strlen(err_msg), 0); + continue; + } + + size_t resp_len = processed_len + (size_t)n; + + if (mq_send(mq_resp, resp_buf, resp_len, 0) == -1) { + fprintf(stderr, "ERROR: mq_send failed: %s\n", + strerror(errno)); + continue; + } + + printf("Response sent: %zu bytes\n\n", resp_len); } - // При нормальном окончании (мёд закончился или Винни умер с голоду) - // посылаем всем клиентам STOP - if (need_stop_broadcast) { - fprintf(stderr, "Server: broadcasting STOP to clients\n"); - send_stop_to_clients(); - msleep(100); // даём клиентам время получить сообщение - } - - // Закрываем и удаляем очередь запросов - mq_close(qreq); - mq_unlink(REQ_QUEUE); - - fprintf(stderr, "Server: finished\n"); + printf("Server shutting down...\n"); + mq_close(mq_req); + mq_close(mq_resp); + mq_unlink(MQ_REQUEST); + mq_unlink(MQ_RESPONSE); return 0; } diff --git a/vlad/lab_5/worker.c b/vlad/lab_5/worker.c deleted file mode 100644 index 27ffd44..0000000 --- a/vlad/lab_5/worker.c +++ /dev/null @@ -1,123 +0,0 @@ -#define _GNU_SOURCE // для определения расширенных возможностей glibc -#include -#include // POSIX очереди сообщений: mq_open, mq_send, mq_receive -#include -#include -#include // fprintf, perror, dprintf -#include // atoi, rand_r -#include // memset, strncpy, snprintf -#include // права для mq_open (0666) -#include // time(), nanosleep, struct timespec -#include // getpid, STDOUT_FILENO - -#include "common.h" // REQ_QUEUE, NAME_MAXLEN, req_msg_t, rep_msg_t - -// Пауза на заданное количество миллисекунд -static void msleep(int ms) { - struct timespec ts = { - .tv_sec = ms / 1000, - .tv_nsec = (ms % 1000) * 1000000L - }; - nanosleep(&ts, NULL); -} - -int main(int argc, char **argv) { - // Ожидается один аргумент: сколько мёда пчела просит за раз - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 2; - } - int portion = atoi(argv[1]); - if (portion <= 0) { - fprintf(stderr, "portion must be >0\n"); - return 2; - } - - // PID пчелы и имя её личной очереди ответов - pid_t me = getpid(); - char replyq[NAME_MAXLEN]; - snprintf(replyq, sizeof(replyq), "/bee_%d", (int) me); - - // На всякий случай удаляем старую очередь с таким именем - mq_unlink(replyq); - - // Атрибуты очереди ответов (куда сервер будет слать rep_msg_t) - struct mq_attr attr; - memset(&attr, 0, sizeof(attr)); - attr.mq_maxmsg = 10; - attr.mq_msgsize = sizeof(rep_msg_t); - - // Создаём очередь ответов пчелы, только для чтения - mqd_t qrep = mq_open(replyq, O_CREAT | O_RDONLY, 0666, &attr); - if (qrep == (mqd_t) -1) { - perror("mq_open reply"); - return 1; - } - - // Открываем очередь запросов к серверу (общая очередь REQ_QUEUE) - mqd_t qreq = -1; - for (int i = 0; i < 50; i++) { - qreq = mq_open(REQ_QUEUE, O_WRONLY); - if (qreq != -1) break; // удалось открыть — выходим из цикла - if (errno != ENOENT) { - // другая ошибка, не "очередь ещё не создана" - perror("mq_open req"); - break; - } - // Если сервер ещё не создал очередь (ENOENT) — подождать и попробовать снова - msleep(100); - } - if (qreq == -1) { - // Не смогли открыть очередь запросов — выходим - perror("mq_open req"); - mq_close(qrep); - mq_unlink(replyq); - return 1; - } - - // Инициализация отдельного генератора случайных чисел для этой пчелы - unsigned seed = (unsigned) (time(NULL) ^ (uintptr_t) me); - - // Основной рабочий цикл пчелы - while (1) { - // Ждём случайное время 100–699 мс перед очередным запросом - int ms = 100 + (rand_r(&seed) % 600); - msleep(ms); - - // Формируем запрос к серверу - req_msg_t req; - memset(&req, 0, sizeof(req)); - req.pid = me; - req.want = portion; // сколько мёда хотим получить - strncpy(req.replyq, replyq, sizeof(req.replyq) - 1); // куда слать ответ - - // Отправляем запрос в очередь REQ_QUEUE - if (mq_send(qreq, (const char *) &req, sizeof(req), 0) == -1) { - perror("mq_send"); - break; - } - - // Ждём ответ от сервера в своей очереди - rep_msg_t rep; - ssize_t rd = mq_receive(qrep, (char *) &rep, sizeof(rep), NULL); - if (rd == -1) { - perror("mq_receive"); - break; - } - - // Если нам больше ничего не дают (granted <= 0) — выходим - if (rep.granted <= 0) { - break; - } - - // Иначе логируем, сколько мёда получили и сколько осталось у сервера - dprintf(STDOUT_FILENO, "Bee %d got %d, remain %d\n", - (int) me, rep.granted, rep.remain); - } - - // Очистка ресурсов: закрываем очереди и удаляем личную очередь ответов - mq_close(qreq); - mq_close(qrep); - mq_unlink(replyq); - return 0; -}