Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 2

.pdf
Скачиваний:
24
Добавлен:
23.12.2021
Размер:
1.17 Mб
Скачать

МИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ,

СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное бюджетное образовательное учреждение высшего образования «Санкт-Петербургский государственный университет телекоммуникаций им. проф. М. А. Бонч-Бруевича»

(СПбГУТ)

Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники

ЛАБОРАТОРНАЯ РАБОТА №2

по дисциплине «Математические методы и вычислительные алгоритмы современных систем связи»

студент гр. ИКПИ-84

_______________

Коваленко Л. А.

преподаватель каф. ПИиВТ

_______________

к.п.н., доцент Коробов С. А.

Санкт-Петербург

2021

ЦЕЛЬ РАБОТЫ

Изучение принципов работы клиент-серверных приложений на основе протоколов TCP и UDP, реализующих функцию передачи файлов.

ПОСТАНОВКА ЗАДАЧИ

Необходимо разработать клиент-серверные приложения на основе протоколов TCP и UDP для передачи файлов.

ХОД РАБОТЫ

Реализация TCP-сервера на языке C приведена в таблице 1.

Таблица 1. Реализация TCP-сервера на языке C

tcp_server.c

//tcp_server.c

//Однопоточная реализация для одного клиента

#include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>

#define PORT 1100 #define QUERY_SIZE 200

#define FILENAME_LENGTH 50 // не включая '\0' #define FILESIZE_LENGTH 20 // не включая '\0'

#define SUCCESS 0 #define ERROR_CODE -1 #define FILE_NOT_FOUND -2

#define INVALID_PACKET_SIZE -3 #define FILE_OPEN_ERROR -4

//Функция отправляет строку в сокет

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1

size_t socket_write(const int socket_fd, const char *buffer, const size_t n)

{

ssize_t bytes = 0;

if ((bytes = write(socket_fd, buffer, n * sizeof(char))) <= ERROR_CODE)

{

perror("write operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число записанных символов

}

//Функция читает строку заданной длины из сокета

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1

size_t socket_read(const int socket_fd, char *buffer, const size_t n)

{

ssize_t bytes = 0;

if ((bytes = read(socket_fd, buffer, n * sizeof(char))) <= ERROR_CODE)

{

perror("read operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число прочитанных символов

}

2

int main(void)

{

//int socket(int domain, int type, int protocol);

//Функция для получения дескриптора конечной точки соединения.

//[1] Параметр domain задает домен соединения: выбирает набор протоколов,

//которые будут использоваться для создания соединения.

//PF_INET -- IPv4 протоколы Интернет.

//[2] Сокет имеет тип type, задающий семантику коммуникации.

//SOCK_STREAM -- обеспечивает создание двусторонних надежных и

//последовательных потоков байтов, поддерживающих соединения.

//[3] Параметр protocol задает конкретный протокол, который работает с сокетом

//IPPROTO_TCP -- протокол TCP (Transmission Control Protocol).

const int socket_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket_fd == ERROR_CODE)

{

perror("cannot create socket"); exit(EXIT_FAILURE);

}

struct sockaddr_in sa; // Описывает сокет для работы с протоколами IP const socklen_t sa_len = sizeof(sa); // Размер структуры

memset(&sa, 0, sa_len); // Заполнение нулями sa.sin_family = AF_INET;

//uint16_t htons(uint16_t hostshort); (Host to Network Short)

//Существует два порядка хранения байтов в слове и двойном слове. Один

//из них называется порядком хоста (host byte order), другой - сетевым порядком

//(network byte order) хранения байтов. При указании IP-адреса и номера порта

//необходимо преобразовать число из порядка хоста в сетевой.

//В случае uint16_t этим занимается функция htons.

sa.sin_port = htons(PORT);

// В случае uint32_t этим занимается функция htonl.

sa.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY -- все адреса локального хоста

//Когда сокет создан (с помощью socket), он существует в пространстве имен

//(семействе адресов), но не имеет назначенного адреса. Для назначения адреса используют

//bind.

//int bind(int socket, const struct sockaddr *address, socklen_t address_len);

//Функция для привязки локального адреса address длиной address_len к сокету sockfd.

if (bind(socket_fd, (struct sockaddr *)&sa, sa_len) == ERROR_CODE)

{

perror("bind failed"); close(socket_fd); exit(EXIT_FAILURE);

}

//int listen(int socket, int backlog);

//Функция для выражения готовности принимать входящие соединения

//и задания размера очереди.

//[1] Дескриптор, ссылающийся на сокет.

//[2] Размер очереди для полностью установленных соединений, ожидающих,

//пока процесс примет их.

if (listen(socket_fd, 1) == ERROR_CODE)

{

perror("listen failed"); close(socket_fd); exit(EXIT_FAILURE);

}

//Запросы:

//STATUS <filename> -- получение статуса файла (доступен либо нет)

//SIZE -- получение размера последнего запрошенного файла

//PACKET SIZE <packet_size> -- установка размера пакета (полезной части данных) для передачи

//

последнего запрошенного файла

//

PACKET [0 <= i <= n_packets - 1] -- получение пакета с индексом i

const char *q_status_s = "STATUS ", *q_size_s = "SIZE", *q_packet_size_s = "PACKET SIZE ", *q_packet_s = "PACKET ";

const size_t q_status_n = strlen(q_status_s), q_size_n = strlen(q_size_s), q_packet_size_n = strlen(q_packet_size_s), q_packet_n = strlen(q_packet_s);

FILE *requested_file = NULL; // Запрашиваемый файл

char query[QUERY_SIZE + 1], *packet = NULL, filename[FILENAME_LENGTH + 1] = ""; long packet_size = 0, file_size = 0, n_packets = 0;

// Получение сообщений от клиентов while (1)

{

//int accept(int socket, struct sockaddr *restrict address,

//socklen_t *restrict address_len);

//Функция для принятия соединения на сокете.

//Функция извлекает первый запрос на соединение из очереди

//ожидающих соединений, создает новый подключенный сокет почти

//с такими же параметрами, что и у s, и выделяет для сокета

3

//новый файловый дескриптор, который и возвращается.

//Новый сокет более не находится в слушающем состоянии.

//[1] Дескриптор, ссылающийся на исходный сокет.

//[2] Адрес другой стороны.

//[3] Размер структуры.

const int connect_fd = accept(socket_fd, NULL, NULL); if (connect_fd == ERROR_CODE)

{

perror("accept failed"); continue;

}

printf("Client accepted\n"); fflush(stdout);

// Цикл отправления файлов текущему клиенту while (1)

{

// Ожидание запроса (20 секунд максимум)

fd_set readfds;

 

FD_ZERO(&readfds);

// Очистка набора дескрипторов

FD_SET(connect_fd, &readfds); // Добавление connect_fd в набор struct timeval tv;

tv.tv_sec = 20; // 20 секунд tv.tv_usec = 0; // 0 микросекунд

if (select(connect_fd + 1, &readfds, NULL, NULL, &tv) <= 0)

{

// Закрытие сессии в случае простоя printf("Session closed (elapsed 20 seconds)\n"); fflush(stdout);

break;

}

// Чтение запроса

size_t symbols = socket_read(connect_fd, query, QUERY_SIZE); query[symbols] = '\0';

// Если запрашивается статус файла

if (strncmp(query, q_status_s, q_status_n) == 0)

{

// Сброс

if (requested_file)

fclose(requested_file), requested_file = NULL; free(packet), packet = NULL;

//Сохранение имени запрошенного файла в filename size_t filename_size = symbols - q_status_n; strncpy(filename, query + q_status_n, filename_size); filename[filename_size] = '\0';

//Отправление статуса файла

socket_write(connect_fd, access(filename, F_OK) == 0 ? "+" : "-", 1);

}

// Если запрашивается размер файла

else if (strncmp(query, q_size_s, q_size_n) == 0)

{

// Сброс

if (requested_file)

fclose(requested_file), requested_file = NULL; free(packet), packet = NULL;

// Попытка открытия файла на чтение

if (!(requested_file = fopen(filename, "r")))

{

socket_write(connect_fd, "-", 1); // Если не получилось, ответ '-' continue;

}

fseek(requested_file, 0L, SEEK_END); // Переход в конец файла

file_size = ftell(requested_file); // Получение текущей позиции в файле fseek(requested_file, 0L, SEEK_SET); // Переход в начало файла

// Отправление размера файла

snprintf(query, FILESIZE_LENGTH, "%ld", file_size); socket_write(connect_fd, query, strlen(query));

}

// Если запрашивается установка размера пакета и запрошенный файл открыт

else if (requested_file && strncmp(query, q_packet_size_s, q_packet_size_n) == 0)

{

// Сброс

free(packet), packet = NULL; // Получение размера пакета

packet_size = atol(query + q_packet_size_n); // Если указан допустимый размер

if (1 <= packet_size && packet_size <= file_size)

4

{

//Выделяем память под пакет (на 1 больше для индикации ошибки: + или -) packet = (char *)malloc((size_t)(packet_size + 1) * sizeof(char));

//Вычисляем число пакетов (с округлением в большую сторону)

n_packets = (file_size + packet_size - 1) / packet_size; socket_write(connect_fd, "+", 1); // Одобрено

}

else // Если указан недопустимый размер socket_write(connect_fd, "-", 1); // Не одобрено

}

// Если запрашивается пакет, запрошенный файл открыт и память для пакета выделена else if (requested_file && packet && strncmp(query, q_packet_s, q_packet_n) == 0)

{

// Получение индекса пакета из запроса

const long packet_i = atol(query + q_packet_n);

if (packet_i < 0 || packet_i >= n_packets) // Если индекс выходит за диапазон

{

socket_write(connect_fd, "-", 1); // Индикация: - (есть ошибка) continue;

}

//Получаем индекс пакета из текущей позиции указателя const long packet_cur = ftell(requested_file) / packet_size;

//Переход по пакетам происходит относительно текущей позиции fseek(requested_file, packet_size * (packet_i - packet_cur), SEEK_CUR);

//Чтение пакета из файла во временную строку

symbols = 1 + fread(packet + 1, sizeof(char), (size_t)packet_size, requested_file); packet[0] = '+'; // Индикация: + (нет ошибки)

// Отправление временной строки в сокет socket_write(connect_fd, packet, symbols);

}

else

{

if (query[0] == 'X')

{

if (requested_file)

fclose(requested_file), requested_file = NULL; free(packet), packet = NULL;

printf("Client disconnected\n"); break;

}

else // Запрос не распознан socket_write(connect_fd, "-", 1);

}

}

//int shutdown(int socket, int how);

//Закрывает все или часть открытых соединений на сокете.

//[1] Дескриптор, ссылающийся на исходный сокет.

//[2] Определяет, что закрыть:

//SHUT_RD -- закрывает все приемы.

//SHUT_WR -- закрывает все передачи.

//SHUT_RDWR -- закрывает все приемы и передачи.

if (shutdown(connect_fd, SHUT_RDWR) == ERROR_CODE)

{

perror("shutdown failed"); close(connect_fd); close(socket_fd); exit(EXIT_FAILURE);

}

close(connect_fd); fflush(stdout);

}

close(socket_fd); return EXIT_SUCCESS;

}

5

Реализация TCP-клиента на языке C приведена в таблице 2.

Таблица 2. Реализация TCP-клиента на языке C

tcp_client.c

// tcp_client.c

#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <time.h> #include <unistd.h>

#define PORT 1100 #define HOST "0.0.0.0" #define QUERY_SIZE 200

#define FILENAME_LENGTH 50 // не включая '\0' #define FILESIZE_LENGTH 20 // не включая '\0' #define DOWNLOAD_PREFIX "download_"

#define SUCCESS 0 #define ERROR_CODE -1 #define FILE_NOT_FOUND -2

#define INVALID_PACKET_SIZE -3 #define FILE_OPEN_ERROR -4

// Функция возвращает процессорное время процесса в секундах

double get_proc_time(void) { return (double)clock() / CLOCKS_PER_SEC; }

//Функция отправляет строку в сокет

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1

size_t socket_write(const int socket_fd, const char *buffer, const size_t n)

{

ssize_t bytes = 0;

if ((bytes = write(socket_fd, buffer, n * sizeof(char))) <= ERROR_CODE)

{

perror("write operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число записанных символов

}

//Функция читает строку заданной длины из сокета

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1

size_t socket_read(const int socket_fd, char *buffer, const size_t n)

{

ssize_t bytes = 0;

if ((bytes = read(socket_fd, buffer, n * sizeof(char))) <= ERROR_CODE)

{

perror("read operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число прочитанных символов

}

//Функция отправляет запрос на получение статуса файла (существует или нет)

//Контракт: socket_fd != -1, filename != nullptr, 1 <= strlen(filename) < FILENAME_LENGTH int socket_get_file_status(const int socket_fd, const char *filename)

{

char query[QUERY_SIZE], b_filename_exists = '-'; snprintf(query, QUERY_SIZE, "STATUS %s", filename); socket_write(socket_fd, query, strlen(query)); socket_read(socket_fd, &b_filename_exists, 1);

return b_filename_exists == '+' ? SUCCESS : FILE_NOT_FOUND;

}

//Функция отправляет запрос на получение размера файла

//Контракт: socket_fd != -1

long socket_get_file_size(const int socket_fd)

6

{

char file_size[FILESIZE_LENGTH + 1]; socket_write(socket_fd, "SIZE", 4);

const size_t symbols = socket_read(socket_fd, file_size, FILESIZE_LENGTH); file_size[symbols] = '\0';

return ('0' <= file_size[0] && file_size[0] <= '9') ? atol(file_size) : FILE_NOT_FOUND;

}

//Функция отправляет запрос на установку размера пакета

//Контракт: socket_fd != -1, 1 <= packet_size <= file_size

int socket_set_packet_size(const int socket_fd, const long packet_size)

{

char query[QUERY_SIZE], b_packet_size = '\0'; snprintf(query, QUERY_SIZE, "PACKET SIZE %ld", packet_size); socket_write(socket_fd, query, strlen(query)); socket_read(socket_fd, &b_packet_size, 1);

return (b_packet_size == '+') ? SUCCESS : INVALID_PACKET_SIZE;

}

//Функция загружает файл в пакетном режиме

//Контракт: socket_fd != -1, filename != nullptr, filename имеет '\0',

//1 <= strlen(filename) < FILENAME_LENGTH, 1 <= packet_size <= file_size

int socket_download_file(const int socket_fd, const char *filename, const long packet_size, const long file_size)

{

FILE *downloaded_file = fopen(filename, "w"); if (!downloaded_file)

{

perror("file open error"); return FILE_OPEN_ERROR;

}

const long n_packets = (file_size + packet_size - 1) / packet_size; // Число пакетов

//Выделяем память под запрос char query[QUERY_SIZE];

//Выделяем память под пакет (на 1 больше для индикации ошибки: + или -) char *packet = (char *)malloc((size_t)(packet_size + 1) * sizeof(char)); for (long i = 0; i < n_packets; ++i)

{

//Формирование запроса на пакет с индексом i

snprintf(query, QUERY_SIZE, "PACKET %ld", i); // Отправление запроса socket_write(socket_fd, query, strlen(query));

//Получение пакета packet[0] = '\0';

size_t symbols = socket_read(socket_fd, packet, (size_t)(packet_size + 1));

//Запись пакета в файл, если он поступил

if (packet[0] == '+' && symbols > 1)

fwrite(packet + 1, symbols - 1, sizeof(char), downloaded_file);

else

printf("Response error: %.*s\n", (int)symbols, packet);

}

free(packet);

return fclose(downloaded_file) == 0 ? SUCCESS : ERROR_CODE;

}

//Функция для очистки потока ввода void clear_stream(FILE *stream)

{

if (stream)

while (fgetc(stream) != '\n') continue;

}

//Функция ввода y/n

//Контракт: s != NULL, s имеет '\0' int input_yes_or_not(const char *s)

{

int ch;

printf("%s [y/n]: ", s);

while ((ch = fgetc(stdin)) != 'y' && ch != 'n')

{

clear_stream(stdin); // Считывает все оставшиеся ошибочно введенные символы printf("%s [y/n]: ", s); // Приглашает к вводу снова

}

clear_stream(stdin);

7

return ch;

}

//Функция ввода числа типа long из диапазона [from, to]

//Контракт: s != NULL, s имеет '\0'

long input_long(const char *s, long from, long to)

{

if (from > to)

{

long temp = from; from = to;

to = temp;

}

for (long r = 0; /* always true */;)

{

printf("%s [%ld, %ld]: ", s, from, to); scanf("%ld", &r);

clear_stream(stdin);

if (from <= r && r <= to) return r;

}

}

//Функция для конкатенации двух строк s1 + s2

//Контракт: память под строку r должна быть освобождена функцией free(),

//s1 != NULL, s2 != NULL, s1 и s2 имеют '\0'

char *concat(const char *s1, const char *s2)

{

const size_t s1_len = strlen(s1);

char *r = (char *)malloc((s1_len + strlen(s2) + 1) * sizeof(char)); strcpy(r, s1);

strcpy(r + s1_len, s2); return r;

}

int main(void)

{

//int socket(int domain, int type, int protocol);

//Функция для получения дескриптора конечной точки соединения.

//[1] Параметр domain задает домен соединения: выбирает набор протоколов,

//которые будут использоваться для создания соединения.

//PF_INET -- IPv4 протоколы Интернет.

//[2] Сокет имеет тип type, задающий семантику коммуникации.

//SOCK_STREAM -- обеспечивает создание двусторонних надежных и

//последовательных потоков байтов, поддерживающих соединения.

//[3] Параметр protocol задает конкретный протокол, который работает с сокетом

//IPPROTO_TCP -- протокол TCP (Transmission Control Protocol).

const int socket_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket_fd == ERROR_CODE)

{

perror("cannot create socket"); exit(EXIT_FAILURE);

}

struct sockaddr_in sa; // Описывает сокет для работы с протоколами IP const socklen_t sa_len = sizeof(sa); // Размер структуры

memset(&sa, 0, sa_len); // Заполнение нулями sa.sin_family = AF_INET;

//uint16_t htons(uint16_t hostshort); (Host to Network Short)

//Существует два порядка хранения байтов в слове и двойном слове. Один

//из них называется порядком хоста (host byte order), другой - сетевым порядком

//(network byte order) хранения байтов. При указании IP-адреса и номера порта

//необходимо преобразовать число из порядка хоста в сетевой.

//В случае uint16_t этим занимается функция htons.

sa.sin_port = htons(PORT);

//int inet_pton(int af, const char *restrict src, void *restrict dst);

//Функция преобразует строку символов src в сетевой адрес (типа af),

//затем копирует полученную структуру с адресом в dst.

//[1] Тип сетевого адреса.

//AF_INET -- сетевой адрес IPv4.

//[2] Строка символов, содержащая сетевой адрес IPv4 в формате "ddd.ddd.ddd.ddd".

//[3] Переменная для получения результирующего числового адреса.

if (inet_pton(AF_INET, HOST, &sa.sin_addr.s_addr) != 1)

{

perror("inet_pton failed"); close(socket_fd); exit(EXIT_FAILURE);

8

}

//int connect(int socket, const struct sockaddr *address, socklen_t address_len);

//Функция инициирует соединение на сокете.

//[1] Дескриптор, ссылающийся на сокет.

//[2] Адрес отправления (в виде структуры).

//[3] Размер структуры.

if (connect(socket_fd, (struct sockaddr *)&sa, sa_len) == ERROR_CODE)

{

perror("connect failed"); close(socket_fd); exit(EXIT_FAILURE);

}

// Цикл загрузки файлов клиентом char filename[FILENAME_LENGTH + 1]; while (1)

{

// Ввод имени файла

printf("Filename (%d symbols max): ", FILENAME_LENGTH); fgets(filename, FILENAME_LENGTH, stdin);

const size_t len = strlen(filename); if (len <= 1) // Если ничего не введено

{

break;

}

if (filename[len - 1] == '\n') // Если не было выхода за пределы FILENAME_LENGTH

{

filename[len - 1] = '\0';

}

else // Если был выход за пределы FILENAME_LENGTH

{

clear_stream(stdin); // Не считавшиеся данные удаляются

// Вручную добавлять '\0' не нужно (fgets делает это автоматически)

}

// Запрос статуса файла

int status = socket_get_file_status(socket_fd, filename); if (status != SUCCESS)

{

printf("File not found\n"); continue;

}

// Запрос размера файла

const long file_size = socket_get_file_size(socket_fd); if (file_size <= 0)

{

printf("Unknown file size\n"); continue;

}

// Нужно ли загружать файл

if (input_yes_or_not("Download") == 'n') continue;

//Запрос установки размера пакета const long packet_size =

input_long("Packet size", 1L, file_size); // Диапазон: [1, file_size] status = socket_set_packet_size(socket_fd, packet_size);

if (status != SUCCESS)

{

printf(status == INVALID_PACKET_SIZE ? "Invalid packet size\n" : "File not found\n"); continue;

}

//Получение пакетов из сокета и запись их в файл

char *new_filename = concat(DOWNLOAD_PREFIX, filename); double start = get_proc_time();

status = socket_download_file(socket_fd, new_filename, packet_size, file_size); double stop = get_proc_time();

printf(status == SUCCESS ? "SUCCESS\n" : "ERROR\n"); printf("Time: %lf seconds\n", stop - start); free(new_filename);

}

// Отправление завершающего соединение сообщения socket_write(socket_fd, "X", 1); close(socket_fd);

return EXIT_SUCCESS;

}

9

Под UDP0 будем понимать реализацию без гарантированной доставки по протоколу UDP.

Реализация UDP0-сервера на языке C приведена в таблице 3.

Таблица 3. Реализация UDP0-сервера на языке C

udp0_server.c

//udp0_server.c

//Однопоточная реализация для одного клиента

#include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #include <time.h> #include <unistd.h>

#define PORT 1100 #define QUERY_SIZE 200

#define FILENAME_LENGTH 50 // не включая '\0' #define FILESIZE_LENGTH 20 // не включая '\0'

#define SUCCESS 0 #define ERROR_CODE -1 #define FILE_NOT_FOUND -2

#define INVALID_PACKET_SIZE -3 #define FILE_OPEN_ERROR -4

//Функция отправляет строку в сокет

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1, sa != nullptr, sa_len == sizeof(*sa)

size_t socket_write(const int socket_fd, const char *buffer_in, const size_t n, struct sockaddr *sa, socklen_t sa_len)

{

ssize_t bytes = 0;

if ((bytes = sendto(socket_fd, buffer_in, n * sizeof(char), 0, sa, sa_len)) <= ERROR_CODE)

{

perror("write operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число записанных символов

}

//Функция читает строку заданной длины из сокета

//Контракт: socket_fd != -1, buffer != nullptr, n >= 1, sa != nullptr, sa_len == sizeof(*sa) size_t socket_read(const int socket_fd, char *buffer, const size_t n, struct sockaddr *sa,

socklen_t sa_len)

{

ssize_t bytes = 0;

if ((bytes = recvfrom(socket_fd, buffer, n * sizeof(char), 0, sa, &sa_len)) <= ERROR_CODE)

{

perror("read operation failed"); close(socket_fd); exit(EXIT_FAILURE);

}

return (size_t)bytes / sizeof(char); // Число прочитанных символов

}

int main(void)

{

srand((unsigned int)time(NULL));

//int socket(int domain, int type, int protocol);

//Функция для получения дескриптора конечной точки соединения.

//[1] Параметр domain задает домен соединения: выбирает набор протоколов,

//которые будут использоваться для создания соединения.

//PF_INET -- IPv4 протоколы Интернет.

//[2] Сокет имеет тип type, задающий семантику коммуникации.

//SOCK_DGRAM -- передача датаграмм (ненадежных сообщений с ограниченной

//длиной и не поддерживающих соединения).

10