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

222 lines
9.1 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: разделяемая память + именованные семафоры.
// Обрабатывает строки по заданию варианта 12:
// "Заменить на пробелы все символы, совпадающие с первым символом в строке,
// кроме самого первого символа."
#include <stdio.h> // printf, fprintf, perror
#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
#include <sys/mman.h> // shm_open, mmap, munmap
#include <sys/stat.h> // mode_t, S_IRUSR, S_IWUSR
#include <semaphore.h> // sem_t, sem_open, sem_close, sem_unlink, sem_wait, sem_post
#include <unistd.h> // ftruncate, close
// Максимальная длина строки для обмена (включая '\0').
#define SHM_BUFFER_SIZE 1024
// Структура данных в разделяемой памяти.
typedef struct {
// Флаг наличия данных от клиента: 0 - нет, 1 - есть.
int has_data;
// Флаг, который сервер выставляет как код возврата:
// 0 - нормальное завершение обработки
// -1 - ошибка обработки (например, некорректные данные).
int result_code;
// Буфер для строки.
char buffer[SHM_BUFFER_SIZE];
} shared_block_t;
// Функция обработки строки по варианту 12.
static void process_line(char *s) {
// Если строка пустая или первый символ - '\0', делать нечего.
if (s == NULL || s[0] == '\0') {
return;
}
// Сохраняем первый символ строки.
char first = s[0];
// Идём по строке начиная со второго символа.
for (size_t i = 1; s[i] != '\0'; ++i) {
// Если текущий символ совпадает с первым, заменяем его на пробел.
if (s[i] == first) {
s[i] = ' ';
}
}
}
// Основная функция сервера.
int main(int argc, char *argv[]) {
// Ожидаем минимум два аргумента:
// argv[1] - имя объекта shared memory (например, "/myshm").
// argv[2] - имя семафора для клиента (например, "/sem_client").
// argv[3] - имя семафора для сервера (например, "/sem_server").
// Дополнительно можно передать количество итераций.
if (argc < 4) {
fprintf(stderr,
"Usage: %s <shm_name> <sem_client_name> <sem_server_name> [iterations]\n",
argv[0]);
// -1 по заданию в случае ошибки.
return -1;
}
// Извлекаем имена объектов POSIX IPC.
const char *shm_name = argv[1];
const char *sem_client_name = argv[2];
const char *sem_server_name = argv[3];
// Количество итераций обработки (по умолчанию - бесконечно).
int iterations = -1;
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;
}
// Создаём или открываем объект разделяемой памяти.
// Используем флаги O_CREAT | O_EXCL | O_RDWR, чтобы сервер был тем,
// кто создаёт объект впервые. В случае существования - ошибка.
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;
}
// Устанавливаем размер объекта разделяемой памяти.
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
perror("ftruncate");
close(shm_fd);
shm_unlink(shm_name);
return -1;
}
// Отображаем объект в адресное пространство процесса.
shared_block_t *shm_ptr = mmap(NULL,
sizeof(shared_block_t),
PROT_READ | PROT_WRITE,
MAP_SHARED,
shm_fd,
0);
if (shm_ptr == MAP_FAILED) {
perror("mmap");
close(shm_fd);
shm_unlink(shm_name);
return -1;
}
// Инициализируем структуру в разделяемой памяти.
shm_ptr->has_data = 0;
shm_ptr->result_code = 0;
memset(shm_ptr->buffer, 0, sizeof(shm_ptr->buffer));
// Закрываем дескриптор файла, отображение уже есть.
if (close(shm_fd) == -1) {
perror("close");
// Не выходим немедленно, поскольку отображение активно.
}
// Создаём именованные семафоры. [web:16]
// sem_client: клиент делает sem_post, сервер делает sem_wait. [web:16]
sem_t *sem_client = sem_open(sem_client_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0);
if (sem_client == SEM_FAILED) {
perror("sem_open(sem_client)");
munmap(shm_ptr, sizeof(shared_block_t));
shm_unlink(shm_name);
return -1;
}
// sem_server: сервер делает sem_post, клиент делает sem_wait. [web:16]
sem_t *sem_server = sem_open(sem_server_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0);
if (sem_server == SEM_FAILED) {
perror("sem_open(sem_server)");
sem_close(sem_client);
sem_unlink(sem_client_name);
munmap(shm_ptr, sizeof(shared_block_t));
shm_unlink(shm_name);
return -1;
}
// Основной цикл обработки.
// Если iterations == -1, цикл бесконечный, прерывается по сигналу.
int processed_count = 0;
while (iterations < 0 || processed_count < iterations) {
// Ожидаем, пока клиент запишет строку и выполнит sem_post(sem_client). [web:16]
if (sem_wait(sem_client) == -1) {
perror("sem_wait(sem_client)");
processed_count = -1;
break;
}
// Проверяем, что клиент записал данные.
if (!shm_ptr->has_data) {
// Некорректное состояние: семафор поднят, но флаг не установлен.
fprintf(stderr, "Warning: sem_client posted, but has_data == 0\n");
shm_ptr->result_code = -1;
} else {
// Обрабатываем строку.
process_line(shm_ptr->buffer);
// В данном примере считаем, что обработка всегда успешна.
shm_ptr->result_code = 0;
// Сбрасываем флаг наличия необработанных данных.
shm_ptr->has_data = 0;
// Увеличиваем счётчик обработанных операций.
processed_count++;
}
// Сообщаем клиенту, что результат готов. [web:16]
if (sem_post(sem_server) == -1) {
perror("sem_post(sem_server)");
processed_count = -1;
break;
}
}
// Выводим количество выполненных операций или -1 в случае ошибки.
if (processed_count >= 0) {
printf("%d\n", processed_count);
} else {
printf("-1\n");
}
// Освобождаем ресурсы: закрываем и удаляем семафоры. [web:16]
if (sem_close(sem_client) == -1) {
perror("sem_close(sem_client)");
}
if (sem_close(sem_server) == -1) {
perror("sem_close(sem_server)");
}
if (sem_unlink(sem_client_name) == -1) {
perror("sem_unlink(sem_client)");
}
if (sem_unlink(sem_server_name) == -1) {
perror("sem_unlink(sem_server)");
}
// Отсоединяем разделяемую память и удаляем объект. [web:8]
if (munmap(shm_ptr, sizeof(shared_block_t)) == -1) {
perror("munmap");
}
if (shm_unlink(shm_name) == -1) {
perror("shm_unlink");
}
return 0;
}