Files
CS-LABS/lab_4/fifo_server.c
2025-11-27 10:23:49 +07:00

205 lines
9.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h> // printf, fprintf
#include <stdlib.h> // malloc, free, strtoll
#include <string.h> // strlen, strerror
#include <unistd.h> // read, write, close, unlink, getpid
#include <fcntl.h> // open, флаги O_RDONLY/O_WRONLY
#include <sys/types.h> // типы для системных вызовов
#include <sys/stat.h> // mkfifo, права доступа
#include <errno.h> // errno
#include <signal.h> // 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 <max_replacements>\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);
// На всякий случай удаляем старые 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>\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;
}