// server_tcp_pairs.c // Лабораторная №8, протокол TCP (нечётные варианты, но здесь по заданию TCP). // Задача: во всех парах одинаковых символов второй символ заменить на пробел, // считать количество замен и сохранить обработанный текст в выходной файл. #include // printf, fprintf, perror, FILE, fopen, fclose, fwrite #include // exit, EXIT_FAILURE, strtol #include // memset, strlen #include // errno #include // close, read, write #include // sockaddr_in, inet_ntoa, htons, htonl, ntohs, ntohl #include // socket, bind, listen, accept #include // типы сокетов #include // структура sockaddr_in #define BUF_SIZE 4096 // Размер буфера приёма из TCP-сокета. // Функция обрабатывает блок данных, заменяя во всех парах одинаковых подряд // идущих символов второй символ пары на пробел. Пары могут пересекать границу // буферов, поэтому учитывается последний символ предыдущего блока. static void process_block_pairs(char *buf, ssize_t len, int *has_prev, unsigned char *prev_char, long long *repl_counter) { if (!buf || !has_prev || !prev_char || !repl_counter) { return; // Проверка входных указателей. } for (ssize_t i = 0; i < len; i++) { unsigned char c = (unsigned char) buf[i]; if (*has_prev) { // Есть предыдущий символ из этого же потока. if (c == *prev_char) { // Найдена пара одинаковых подряд символов. buf[i] = ' '; // Заменяем второй символ пары на пробел. (*repl_counter)++; // Увеличиваем счётчик замен. } *has_prev = 0; // Сбрасываем флаг: пара уже обработана. } else { // Текущий символ станет предыдущим для возможной пары в следующем байте. *prev_char = c; *has_prev = 1; } } } int main(int argc, char *argv[]) { // Ожидаемые аргументы: // argv[1] - порт TCP сервера (например, 5000). // argv[2] - имя выходного файла. if (argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); fprintf(stderr, "Example: %s 5000 out.txt\n", argv[0]); return -1; } // Разбор номера порта. char *endptr = NULL; errno = 0; long port_long = strtol(argv[1], &endptr, 10); if (errno != 0 || endptr == argv[1] || *endptr != '\0' || port_long <= 0 || port_long > 65535) { fprintf(stderr, "ERROR: invalid port '%s'\n", argv[1]); return -1; } int port = (int) port_long; const char *out_path = argv[2]; // Открываем выходной файл для записи (перезаписываем). FILE *fout = fopen(out_path, "w"); if (!fout) { perror("fopen(output_file)"); return -1; } // Создаём TCP-сокет. int listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) { perror("socket"); fclose(fout); return -1; } // Включаем SO_REUSEADDR, чтобы можно было быстро перезапускать сервер. int optval = 1; if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { perror("setsockopt(SO_REUSEADDR)"); close(listen_fd); fclose(fout); return -1; } // Заполняем структуру адреса сервера. struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // Принимать на всех интерфейсах. servaddr.sin_port = htons(port); // Привязываем сокет к адресу и порту. if (bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { perror("bind"); close(listen_fd); fclose(fout); return -1; } // Переводим сокет в режим прослушивания. if (listen(listen_fd, 1) < 0) { perror("listen"); close(listen_fd); fclose(fout); return -1; } printf("TCP server (pairs) listening on port %d, output file: %s\n", port, out_path); // Принимаем одно соединение от клиента. struct sockaddr_in cliaddr; socklen_t cli_len = sizeof(cliaddr); int conn_fd = accept(listen_fd, (struct sockaddr *) &cliaddr, &cli_len); if (conn_fd < 0) { perror("accept"); close(listen_fd); fclose(fout); return -1; } printf("Client connected from %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); close(listen_fd); // Больше новых клиентов не принимаем. char buf[BUF_SIZE]; long long repl_counter = 0; // Общее количество замен. int has_prev = 0; // Был ли предыдущий символ для пары. unsigned char prev_char = 0; // Предыдущий символ, если has_prev == 1. for (;;) { // Считываем данные из TCP-сокета. ssize_t n = read(conn_fd, buf, sizeof(buf)); if (n > 0) { // Обрабатываем блок на предмет пар одинаковых символов. process_block_pairs(buf, n, &has_prev, &prev_char, &repl_counter); // Пишем обработанный блок в выходной файл. if (fwrite(buf, 1, (size_t) n, fout) != (size_t) n) { perror("fwrite"); repl_counter = -1; break; } } else if (n == 0) { // Клиент корректно закрыл соединение. printf("Client disconnected.\n"); break; } else { // Ошибка при чтении. perror("read"); repl_counter = -1; break; } } // Закрываем соединение. close(conn_fd); // Закрываем файл. if (fclose(fout) == EOF) { perror("fclose(output_file)"); repl_counter = -1; } printf("Total replacements (pairs): %lld\n", repl_counter); if (repl_counter < 0) { return -1; } return 0; }