// UDP‑сервер: принимает текст по сети, построчно обрабатывает и пишет в файл. #include #include #include #include #include #include // sockaddr_in, htons, htonl, ntohs, ntohl #include // socket, bind, recvfrom #include #define BUF_SIZE 4096 #define END_MARKER "END_OF_TRANSMISSION" // Обработка одной строки по условию: заменяет на пробелы // все символы, совпадающие с первым, кроме самого первого. static void process_line(char *line, long long *repl_counter) { if (!line || !repl_counter) { return; } size_t len = strlen(line); if (len == 0) { return; } char first = line[0]; for (size_t i = 1; i < len; ++i) { if (line[i] == first && line[i] != '\n') { line[i] = ' '; (*repl_counter)++; } } } int main(int argc, char *argv[]) { // argv[1] — порт сервера UDP, 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; } // Создаём UDP‑сокет: AF_INET (IPv4), SOCK_DGRAM (UDP). int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); fclose(fout); return -1; } // Заполняем адрес сервера и привязываем сокет к порту (bind). struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; // IPv4. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // принимать на всех интерфейсах. servaddr.sin_port = htons(port); // порт в сетевом порядке байт. if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { perror("bind"); close(sockfd); fclose(fout); return -1; } printf("UDP server is listening on port %d, output file: %s\n", port, out_path); char buf[BUF_SIZE]; // сюда прилетают UDP‑датаграммы. char line_buf[BUF_SIZE]; // здесь накапливаем одну логическую строку. size_t line_len = 0; long long total_replacements = 0; int done = 0; // флаг завершения по END_MARKER. while (!done) { struct sockaddr_in cliaddr; socklen_t cli_len = sizeof(cliaddr); // recvfrom читает один UDP‑пакет и заполняет адрес отправителя. ssize_t n = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &cliaddr, &cli_len); if (n < 0) { perror("recvfrom"); total_replacements = -1; break; } buf[n] = '\0'; // удобнее работать как со строкой. // Специальный маркер конца передачи от клиента. if (strcmp(buf, END_MARKER) == 0) { printf("Received END marker, finishing.\n"); done = 1; break; } // Разбор принятого блока посимвольно, сборка полных строк по '\n'. size_t pos = 0; while (pos < (size_t) n) { char c = buf[pos++]; if (c == '\n') { // Закончили строку в line_buf. line_buf[line_len] = '\0'; process_line(line_buf, &total_replacements); if (fprintf(fout, "%s\n", line_buf) < 0) { perror("fprintf"); total_replacements = -1; done = 1; break; } line_len = 0; } else { // Продолжаем накапливать строку. if (line_len + 1 < sizeof(line_buf)) { line_buf[line_len++] = c; } else { fprintf(stderr, "Line buffer overflow, truncating line\n"); } } } } // Если соединение завершилось не на границе строки — // доробатываем последний неполный хвост в line_buf. if (line_len > 0 && total_replacements >= 0) { line_buf[line_len] = '\0'; process_line(line_buf, &total_replacements); if (fprintf(fout, "%s\n", line_buf) < 0) { perror("fprintf"); total_replacements = -1; } } if (fclose(fout) == EOF) { perror("fclose(output_file)"); total_replacements = -1; } close(sockfd); printf("Total replacements: %lld\n", total_replacements); if (total_replacements < 0) { return -1; } return 0; }