#include // printf, fprintf #include // malloc, free, strtoll #include // strlen, strerror #include // read, write, close, unlink, getpid #include // open, флаги O_RDONLY/O_WRONLY #include // типы для системных вызовов #include // mkfifo, права доступа #include // errno #include // signal, SIGINT, SIGTERM #define FIFO_REQUEST "/tmp/fifo_request" // путь к FIFO для запроса клиент→сервер #define FIFO_RESPONSE "/tmp/fifo_response" // путь к FIFO для ответа сервер→клиент #define BUFFER_SIZE 4096 // размер буфера для обмена данными // Флаг для аккуратного завершения по сигналу volatile sig_atomic_t running = 1; // Обработчик сигналов завершения (Ctrl+C, SIGTERM) void signal_handler(int sig) { (void) sig; // чтобы не ругался компилятор на неиспользуемый arg running = 0; // помечаем, что надо выйти из основного цикла } // Разбор аргумента командной строки как неотрицательного long long static long long parse_ll(const char *s) { char *end = NULL; errno = 0; long long v = strtoll(s, &end, 10); // Проверяем: ошибка конверсии, пустая строка, лишние символы или отрицательное число if (errno != 0 || end == s || *end != '\0' || v < 0) { return -1; } return v; } // Основная логика обработки текста: // - input/input_len: входные данные // - output/output_size: буфер для результата // - max_replacements: глобальное ограничение по числу замен // Правило: в каждой строке первый символ принят как "ключ" строки, // все его повторения в этой строке заменяются на пробел (пока не кончится лимит). long long process_data(const char *input, size_t input_len, char *output, size_t output_size, long long max_replacements) { long long total_replacements = 0; // сколько замен уже сделано int at_line_start = 1; // находимся ли в начале строки unsigned char line_key = 0; // символ-ключ для текущей строки size_t out_pos = 0; // позиция записи в выходном буфере // Идём по входным байтам, следим за пределами выходного буфера for (size_t i = 0; i < input_len && out_pos < output_size - 1; i++) { unsigned char c = (unsigned char) input[i]; // В начале строки первый символ становится ключом строки if (at_line_start) { line_key = c; at_line_start = 0; } unsigned char outc = c; // Если встретили перевод строки — следующая позиция снова "начало строки" if (c == '\n') { at_line_start = 1; } // Если символ равен ключу строки и лимит замен ещё не исчерпан — // заменяем его на пробел и увеличиваем счётчик else if (c == line_key && total_replacements < max_replacements) { outc = ' '; total_replacements++; } // Записываем преобразованный символ в выходной буфер output[out_pos++] = (char) outc; } // Добавляем завершающий ноль, чтобы удобнее работать как со строкой output[out_pos] = '\0'; return total_replacements; } int main(int argc, char *argv[]) { // Ожидаем один аргумент: max_replacements if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } // Парсим максимальное количество замен long long max_replacements = parse_ll(argv[1]); if (max_replacements < 0) { fprintf(stderr, "ERROR: Invalid max_replacements\n"); return 1; } // Информационный вывод о запуске сервера printf("=== FIFO Server запущен ===\n"); printf("Server PID: %d\n", getpid()); printf("Максимум замен: %lld\n", max_replacements); // Устанавливаем обработчики сигналов для корректного завершения signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // Не умирать от SIGPIPE, когда клиент закрыл FIFO signal(SIGPIPE, SIG_IGN); // На всякий случай удаляем старые FIFO, если остались от предыдущего запуска unlink(FIFO_REQUEST); unlink(FIFO_RESPONSE); // Создаем именованный канал для запросов if (mkfifo(FIFO_REQUEST, 0666) == -1) { perror("mkfifo request"); return 1; } // Создаем именованный канал для ответов if (mkfifo(FIFO_RESPONSE, 0666) == -1) { perror("mkfifo response"); unlink(FIFO_REQUEST); // чистим первый, если второй mkfifo не удался return 1; } printf("FIFO каналы созданы:\n"); printf(" Request: %s\n", FIFO_REQUEST); printf(" Response: %s\n", FIFO_RESPONSE); printf("Ожидание запросов от клиентов...\n\n"); // Главный цикл сервера: пока не пришёл сигнал завершения while (running) { // Открываем FIFO запроса для чтения (блокируется, пока клиент не откроет на запись) int fd_req = open(FIFO_REQUEST, O_RDONLY); if (fd_req == -1) { // Если нас прервали сигналом, просто пробуем снова if (errno == EINTR) continue; perror("open request FIFO"); break; } // Выделяем буферы под входные и выходные данные для текущего запроса char *input_buffer = malloc(BUFFER_SIZE); char *output_buffer = malloc(BUFFER_SIZE); if (!input_buffer || !output_buffer) { fprintf(stderr, "ERROR: Memory allocation failed\n"); close(fd_req); free(input_buffer); free(output_buffer); continue; } // Читаем данные от клиента ssize_t bytes_read = read(fd_req, input_buffer, BUFFER_SIZE - 1); close(fd_req); // Если ничего не прочитали или ошибка — просто переходим к следующему циклу if (bytes_read <= 0) { free(input_buffer); free(output_buffer); continue; } // Делаем входной буфер C-строкой для удобства отладки input_buffer[bytes_read] = '\0'; printf("Получен запрос: %zd байт\n", bytes_read); // Обрабатываем данные по заданному правилу long long replacements = process_data(input_buffer, bytes_read, output_buffer, BUFFER_SIZE, max_replacements); printf("Выполнено замен: %lld\n", replacements); // Открываем FIFO ответа для записи (ждём, пока клиент откроет на чтение) int fd_resp = open(FIFO_RESPONSE, O_WRONLY); if (fd_resp == -1) { perror("open response FIFO"); free(input_buffer); free(output_buffer); continue; } // Отправляем клиенту обработанный текст size_t output_len = strlen(output_buffer); ssize_t bytes_written = write(fd_resp, output_buffer, output_len); // Затем — служебную строку с количеством замен в формате "\nREPLACEMENTS:\n" char result[64]; snprintf(result, sizeof(result), "\nREPLACEMENTS:%lld\n", replacements); write(fd_resp, result, strlen(result)); // Закрываем FIFO ответа — завершение "сессии" с клиентом close(fd_resp); printf("Отправлен ответ: %zd байт\n\n", bytes_written); // Освобождаем буферы для этого запроса free(input_buffer); free(output_buffer); } // При завершении работы сервера удаляем FIFO printf("\nЗавершение работы сервера...\n"); unlink(FIFO_REQUEST); unlink(FIFO_RESPONSE); return 0; }