Files
CS-LABS/mine/lab_6/server.c
2025-12-11 09:11:32 +07:00

287 lines
10 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.
// Сервер POSIX IPC: использует POSIX shared memory (shm_open + mmap)
// и именованные POSIX семафоры (sem_open, sem_wait, sem_post) для обмена с клиентом.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h> // O_CREAT, O_EXCL, O_RDWR для shm_open
#include <sys/mman.h> // shm_open, mmap, munmap — POSIX разделяемая память
#include <sys/stat.h> // Права доступа к объектам POSIX shared memory и семафорам
#include <semaphore.h> // POSIX именованные семафоры: sem_open, sem_close, sem_unlink, sem_wait, sem_post
#include <unistd.h> // ftruncate, close
#include <signal.h>
// Максимальная длина строки (включая '\0') для обмена через shared memory.
#define SHM_BUFFER_SIZE 1024
// Структура расположена целиком в POSIX shared memory и разделяется сервером и клиентом.
typedef struct {
int has_data;
int result_code;
char buffer[SHM_BUFFER_SIZE];
} shared_block_t;
// Глобальные объекты, связанные с POSIX shared memory и семафорами.
static shared_block_t *g_shm_ptr = NULL; // Указатель на mmap(shared memory).
static char g_shm_name[256] = ""; // Имя объекта POSIX shared memory (для shm_unlink).
static char g_sem_client_name[256] = ""; // Имя именованного семафора клиента (sem_open/sem_unlink).
static char g_sem_server_name[256] = ""; // Имя именованного семафора сервера (sem_open/sem_unlink).
static sem_t *g_sem_client = NULL; // Дескриптор POSIX семафора клиента.
static sem_t *g_sem_server = NULL; // Дескриптор POSIX семафора сервера.
static FILE *g_fout = NULL;
// Обработчик сигналов завершения: корректно удаляет POSIX shared memory и семафоры.
static void handle_signal(int signo) {
(void) signo;
if (g_sem_client) {
sem_close(g_sem_client);
g_sem_client = NULL;
}
if (g_sem_server) {
sem_close(g_sem_server);
g_sem_server = NULL;
}
// sem_unlink удаляет именованные POSIX семафоры из системы.
if (g_sem_client_name[0] != '\0') {
sem_unlink(g_sem_client_name);
}
if (g_sem_server_name[0] != '\0') {
sem_unlink(g_sem_server_name);
}
// munmap отсоединяет область POSIX shared memory из адресного пространства процесса.
if (g_shm_ptr) {
munmap(g_shm_ptr, sizeof(shared_block_t));
g_shm_ptr = NULL;
}
// shm_unlink удаляет объект POSIX shared memory.
if (g_shm_name[0] != '\0') {
shm_unlink(g_shm_name);
}
if (g_fout) {
fclose(g_fout);
g_fout = NULL;
}
_exit(0);
}
static void process_line(char *s) {
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] - имя POSIX 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]);
return -1;
}
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;
const char *sem_client_name = g_sem_client_name;
const char *sem_server_name = g_sem_server_name;
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;
}
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
// Удаляем старые объекты POSIX shared memory и семафоров, если они остались от прошлых запусков.
shm_unlink(shm_name);
sem_unlink(sem_client_name);
sem_unlink(sem_server_name);
// Создаём новый объект POSIX shared memory (shm_open с 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;
}
// ftruncate задаёт размер объекта POSIX shared memory под нашу структуру.
if (ftruncate(shm_fd, sizeof(shared_block_t)) == -1) {
perror("ftruncate");
close(shm_fd);
shm_unlink(shm_name);
return -1;
}
// Отображаем POSIX shared memory в адресное пространство сервера (mmap с MAP_SHARED).
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 файловый дескриптор POSIX shared memory можно закрыть.
if (close(shm_fd) == -1) {
perror("close");
}
g_shm_ptr->has_data = 0;
g_shm_ptr->result_code = 0;
memset(g_shm_ptr->buffer, 0, sizeof(g_shm_ptr->buffer));
// Создаём именованный семафор клиента: клиент будет делать sem_post, сервер — sem_wait.
g_sem_client = sem_open(sem_client_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0); // начальное значение 0, сервер сразу блокируется на sem_wait.
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;
}
// Создаём именованный семафор сервера: сервер делает sem_post, клиент — sem_wait.
g_sem_server = sem_open(sem_server_name,
O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
0);
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;
}
g_fout = fopen("output.txt", "w");
if (!g_fout) {
perror("fopen(output.txt)");
}
int processed_count = 0;
while (iterations < 0 || processed_count < iterations) {
// Сервер блокируется на семафоре клиента (sem_wait), ожидая данных в shared memory.
if (sem_wait(g_sem_client) == -1) {
perror("sem_wait(sem_client)");
processed_count = -1;
break;
}
if (!g_shm_ptr->has_data) {
fprintf(stderr,
"Warning: sem_client posted, but has_data == 0\n");
g_shm_ptr->result_code = -1;
} else {
process_line(g_shm_ptr->buffer);
g_shm_ptr->result_code = 0;
if (g_fout) {
fprintf(g_fout, "%s\n", g_shm_ptr->buffer);
fflush(g_fout);
}
g_shm_ptr->has_data = 0;
processed_count++;
}
// Сервер увеличивает семафор сервера (sem_post), разблокируя клиента (sem_wait).
if (sem_post(g_sem_server) == -1) {
perror("sem_post(sem_server)");
processed_count = -1;
break;
}
}
if (g_fout) {
if (fclose(g_fout) == EOF) {
perror("fclose(output.txt)");
}
g_fout = NULL;
}
printf("%d\n", processed_count >= 0 ? processed_count : -1);
// Закрываем дескрипторы POSIX семафоров в процессе сервера.
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;
}
// sem_unlink полностью удаляет именованные семафоры из системы.
if (sem_unlink(sem_client_name) == -1) {
perror("sem_unlink(sem_client)");
}
if (sem_unlink(sem_server_name) == -1) {
perror("sem_unlink(sem_server)");
}
// Отсоединяем область POSIX shared memory и удаляем сам объект.
if (g_shm_ptr) {
if (munmap(g_shm_ptr, sizeof(shared_block_t)) == -1) {
perror("munmap");
}
g_shm_ptr = NULL;
}
if (shm_unlink(shm_name) == -1) {
perror("shm_unlink");
}
return 0;
}