Files
CS-LABS/lab_6/server.c
2025-12-10 14:33:23 +07:00

320 lines
14 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.
// server.c
// Сервер POSIX IPC: разделяемая память + именованные семафоры. [file:21]
// - Сам удаляет "висячие" объекты (shm и семафоры) при старте. [file:21]
// - Создаёт новую shared memory и новые именованные семафоры. [web:47][web:40]
// - Обрабатывает строки по варианту 12 и пишет результат в shared memory и output.txt. [file:22]
#include <stdio.h> // printf, fprintf, perror, FILE, fopen, fclose, fprintf [web:60]
#include <stdlib.h> // exit, EXIT_FAILURE, strtoul
#include <string.h> // memset, strncpy, strlen
#include <errno.h> // errno
#include <fcntl.h> // O_CREAT, O_EXCL, O_RDWR [web:29]
#include <sys/mman.h> // shm_open, mmap, munmap [web:47]
#include <sys/stat.h> // mode_t, S_IRUSR, S_IWUSR [web:39]
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post [file:21]
#include <unistd.h> // ftruncate, close [file:21]
#include <signal.h> // signal, SIGINT, SIGTERM
// Максимальная длина строки (включая '\0') для обмена через shared memory. [file:21]
#define SHM_BUFFER_SIZE 1024
// Структура в разделяемой памяти. [file:21]
typedef struct {
int has_data; // 0 - нет данных, 1 - клиент записал строку. [file:21]
int result_code; // 0 - успех, -1 - ошибка обработки. [file:21]
char buffer[SHM_BUFFER_SIZE]; // Буфер, в который клиент пишет, а сервер изменяет. [file:21]
} shared_block_t;
// Глобальные указатели/имена для корректной очистки в обработчике сигналов. [file:21]
static shared_block_t *g_shm_ptr = NULL; // Указатель на mmap(shared memory). [web:47]
static char g_shm_name[256] = ""; // Имя объекта shared memory. [file:21]
static char g_sem_client_name[256] = ""; // Имя семафора клиента. [file:21]
static char g_sem_server_name[256] = ""; // Имя семафора сервера. [file:21]
static sem_t *g_sem_client = NULL; // Дескриптор семафора клиента. [file:21]
static sem_t *g_sem_server = NULL; // Дескриптор семафора сервера. [file:21]
static FILE *g_fout = NULL; // Файл output.txt. [web:57]
// Обработчик сигналов для аккуратного завершения (Ctrl+C, kill). [file:21]
static void handle_signal(int signo) {
(void) signo; // Неиспользуемый параметр, подавляем предупреждения. [file:21]
// Пытаемся корректно закрыть семафоры, если они открыты. [file:21]
if (g_sem_client) {
sem_close(g_sem_client); // Закрываем дескриптор семафора клиента. [file:21]
g_sem_client = NULL;
}
if (g_sem_server) {
sem_close(g_sem_server); // Закрываем дескриптор семафора сервера. [file:21]
g_sem_server = NULL;
}
// Удаляем именованные семафоры по их именам, если они заданы. [file:21][web:40]
if (g_sem_client_name[0] != '\0') {
sem_unlink(g_sem_client_name); // Удаляем именованный семафор клиента. [web:40]
}
if (g_sem_server_name[0] != '\0') {
sem_unlink(g_sem_server_name); // Удаляем именованный семафор сервера. [web:40]
}
// Если есть активное отображение shared memory, отсоединяем его. [web:47]
if (g_shm_ptr) {
munmap(g_shm_ptr, sizeof(shared_block_t)); // Освобождаем виртуальную память. [web:47]
g_shm_ptr = NULL;
}
// Удаляем объект shared memory, если имя известно. [web:47]
if (g_shm_name[0] != '\0') {
shm_unlink(g_shm_name); // Удаляем объект POSIX shared memory. [web:47]
}
// Закрываем файл вывода, если он открыт. [web:57]
if (g_fout) {
fclose(g_fout); // Закрываем output.txt. [web:57]
g_fout = NULL;
}
// Завершаем процесс. [file:21]
_exit(0);
}
// Обработка строки по варианту 12. [file:22]
static void process_line(char *s) {
if (s == NULL || s[0] == '\0') {
// Пустой буфер или пустая строка не обрабатываем. [file:21]
return;
}
char first = s[0]; // Сохраняем первый символ. [file:22]
// Проходим по строке начиная со второго символа. [file:22]
for (size_t i = 1; s[i] != '\0'; ++i) {
if (s[i] == first) {
// Если символ совпадает с первым, заменяем его на пробел. [file:22]
s[i] = ' ';
}
}
}
int main(int argc, char *argv[]) {
// Ожидаемые аргументы:
// argv[1] - имя shared memory (например, "/myshm"). [file:21]
// argv[2] - имя семафора клиента (например, "/sem_client"). [file:21]
// argv[3] - имя семафора сервера (например, "/sem_server"). [file:21]
// argv[4] - (опционально) число обрабатываемых строк. [file:22]
if (argc < 4) {
fprintf(stderr,
"Usage: %s <shm_name> <sem_client_name> <sem_server_name> [iterations]\n",
argv[0]); // Выводим подсказку по использованию. [file:22]
return -1; // По заданию -1 при ошибке. [file:22]
}
// Сохраняем имена в глобальные переменные для очистки в сигналах. [file:21]
strncpy(g_shm_name, argv[1], sizeof(g_shm_name) - 1);
strncpy(g_sem_client_name, argv[2], sizeof(g_sem_client_name) - 1);
strncpy(g_sem_server_name, argv[3], sizeof(g_sem_server_name) - 1);
g_shm_name[sizeof(g_shm_name) - 1] = '\0';
g_sem_client_name[sizeof(g_sem_client_name) - 1] = '\0';
g_sem_server_name[sizeof(g_sem_server_name) - 1] = '\0';
const char *shm_name = g_shm_name; // Удобные локальные псевдонимы. [file:21]
const char *sem_client_name = g_sem_client_name;
const char *sem_server_name = g_sem_server_name;
// Разбираем количество итераций (строк), если указано. [file:22]
int iterations = -1; // -1 означает "работать до сигнала". [file:22]
if (argc >= 5) {
char *endptr = NULL;
unsigned long tmp = strtoul(argv[4], &endptr, 10);
if (endptr == argv[4] || *endptr != '\0') {
fprintf(stderr, "Invalid iterations value: '%s'\n", argv[4]);
return -1;
}
iterations = (int) tmp;
}
// Настраиваем обработчики сигналов для аккуратного завершения. [file:21]
signal(SIGINT, handle_signal); // Обработка Ctrl+C. [file:21]
signal(SIGTERM, handle_signal); // Обработка внешнего kill. [file:21]
// Пытаемся удалить старые IPC-объекты, если они остались от прошлых запусков. [file:21]
shm_unlink(shm_name); // Игнорируем ошибку, если не существует. [web:47]
sem_unlink(sem_client_name); // То же для семафоров. [web:40]
sem_unlink(sem_server_name);
// Создаём новый объект shared memory. [web:47]
int shm_fd = shm_open(shm_name,
O_CREAT | O_EXCL | O_RDWR,
S_IRUSR | S_IWUSR);
if (shm_fd == -1) {
perror("shm_open");
return -1;
}
// Устанавливаем размер shared memory под нашу структуру. [file:21]
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
perror("ftruncate");
close(shm_fd);
shm_unlink(shm_name);
return -1;
}
// Отображаем shared memory в адресное пространство сервера. [web:47]
g_shm_ptr = mmap(NULL,
sizeof(shared_block_t),
PROT_READ | PROT_WRITE,
MAP_SHARED,
shm_fd,
0);
if (g_shm_ptr == MAP_FAILED) {
perror("mmap");
g_shm_ptr = NULL;
close(shm_fd);
shm_unlink(shm_name);
return -1;
}
// Закрываем файловый дескриптор, оставляем только mmap. [file:21]
if (close(shm_fd) == -1) {
perror("close");
// Продолжаем работу, так как mmap уже есть. [file:21]
}
// Инициализируем структуру в shared memory. [file:21]
g_shm_ptr->has_data = 0;
g_shm_ptr->result_code = 0;
memset(g_shm_ptr->buffer, 0, sizeof(g_shm_ptr->buffer));
// Создаём именованный семафор клиента (клиент будет делать post, сервер - wait). [file:21][web:40]
g_sem_client = sem_open(sem_client_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0); // Начальное значение 0. [file:21]
if (g_sem_client == SEM_FAILED) {
perror("sem_open(sem_client)");
g_sem_client = NULL;
munmap(g_shm_ptr, sizeof(shared_block_t));
g_shm_ptr = NULL;
shm_unlink(shm_name);
return -1;
}
// Создаём именованный семафор сервера (сервер post, клиент wait). [file:21][web:40]
g_sem_server = sem_open(sem_server_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0); // Начальное значение 0. [file:21]
if (g_sem_server == SEM_FAILED) {
perror("sem_open(sem_server)");
sem_close(g_sem_client);
sem_unlink(sem_client_name);
g_sem_client = NULL;
munmap(g_shm_ptr, sizeof(shared_block_t));
g_shm_ptr = NULL;
shm_unlink(shm_name);
return -1;
}
// Открываем файл вывода для записи всех обработанных строк. [web:57]
g_fout = fopen("output.txt", "w");
if (!g_fout) {
perror("fopen(output.txt)");
// Если файл не открылся, продолжаем работать только через shared memory. [file:21]
}
int processed_count = 0; // Счётчик успешно обработанных строк. [file:22]
// Основной цикл обработки запросов от клиента. [file:22]
while (iterations < 0 || processed_count < iterations) {
// Ждём, пока клиент поднимет семафор клиента. [file:21][web:40]
if (sem_wait(g_sem_client) == -1) {
perror("sem_wait(sem_client)");
processed_count = -1;
break;
}
// Проверяем, что клиент установил флаг has_data. [file:21]
if (!g_shm_ptr->has_data) {
fprintf(stderr,
"Warning: sem_client posted, but has_data == 0\n");
g_shm_ptr->result_code = -1;
} else {
// Обрабатываем строку по варианту 12. [file:22]
process_line(g_shm_ptr->buffer);
// Сохраняем код успешной обработки. [file:21]
g_shm_ptr->result_code = 0;
// Если файл открыт, дублируем результат в output.txt. [web:57]
if (g_fout) {
fprintf(g_fout, "%s\n", g_shm_ptr->buffer);
fflush(g_fout);
}
// Сбрасываем флаг наличия данных. [file:21]
g_shm_ptr->has_data = 0;
// Увеличиваем счётчик обработанных строк. [file:22]
processed_count++;
}
// Сигнализируем клиенту, что результат готов. [file:21][web:40]
if (sem_post(g_sem_server) == -1) {
perror("sem_post(sem_server)");
processed_count = -1;
break;
}
}
// Если файл вывода был открыт, закрываем его. [web:57]
if (g_fout) {
if (fclose(g_fout) == EOF) {
perror("fclose(output.txt)");
}
g_fout = NULL;
}
// Выводим количество выполненных операций или -1 при ошибке. [file:22]
if (processed_count >= 0) {
printf("%d\n", processed_count);
} else {
printf("-1\n");
}
// Корректно закрываем семафоры. [file:21]
if (g_sem_client) {
if (sem_close(g_sem_client) == -1) {
perror("sem_close(sem_client)");
}
g_sem_client = NULL;
}
if (g_sem_server) {
if (sem_close(g_sem_server) == -1) {
perror("sem_close(sem_server)");
}
g_sem_server = NULL;
}
// Удаляем именованные семафоры. [file:21][web:40]
if (sem_unlink(sem_client_name) == -1) {
perror("sem_unlink(sem_client)");
}
if (sem_unlink(sem_server_name) == -1) {
perror("sem_unlink(sem_server)");
}
// Отсоединяем shared memory. [web:47]
if (g_shm_ptr) {
if (munmap(g_shm_ptr, sizeof(shared_block_t)) == -1) {
perror("munmap");
}
g_shm_ptr = NULL;
}
// Удаляем объект shared memory. [web:47]
if (shm_unlink(shm_name) == -1) {
perror("shm_unlink");
}
return 0; // Нормальное завершение сервера. [file:22]
}