/
bvs22
/
draft
Обзор
Документация
Войти
/
bvs22
/
draft
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
network/udp
263 строки
10 KB
Batomunkuev Vladimir
1
01 фев 2025, 08:19
01 фев 2025, 08:19
ed6e22e
Код
Авторство
О чём код?
### Подробный разбор функций для работы с UDP-сокетами на языке C UDP (User Datagram Protocol) — это протокол передачи данных, который работает без установления соединения. Он предоставляет ненадежную доставку пакетов, но обеспечивает более быструю передачу данных по сравнению с TCP. В этом разделе мы подробно рассмотрим основные функции для работы с UDP-сокетами. #### Основные функции для работы с UDP-сокетами 1. **`socket()`** 2. **`bind()`** 3. **`sendto()`** 4. **`recvfrom()`** 5. **`close()`** ### 1. `socket(int domain, int type, int protocol)` **Описание**: Создает новый сокет. **Аргументы**: - **`domain`**: Адресное пространство (или семейство протоколов). - **`AF_INET`**: IPv4. - **`AF_INET6`**: IPv6. - **`type`**: Тип сокета. - **`SOCK_DGRAM`**: Датаграммный сокет (используется для UDP). - **`protocol`**: Протокол, который будет использоваться с этим сокетом. - Обычно используется значение `0`, что означает использование стандартного протокола для данного типа сокета (например, UDP для `SOCK_DGRAM`). **Пример**: ```c int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } ``` ### 2. `bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)` **Описание**: Привязывает сокет к определенному адресу и порту. **Аргументы**: - **`sockfd`**: Дескриптор сокета, созданный функцией `socket()`. - **`addr`**: Указатель на структуру `sockaddr`, содержащую информацию об адресе и порте. - Для IPv4 это структура `sockaddr_in`. - Пример заполнения: ```c struct sockaddr_in address; address.sin_family = AF_INET; // Используем IPv4 address.sin_addr.s_addr = INADDR_ANY; // Принимать соединения с любого IP address.sin_port = htons(PORT); // Преобразование порта в сетевой порядок байтов ``` - **`addrlen`**: Размер структуры `sockaddr`. **Пример**: ```c struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); if (bind(sockfd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 3. `sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)` **Описание**: Отправляет данные через сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. - **`buf`**: Указатель на буфер с данными для отправки. - **`len`**: Количество байт для отправки. - **`flags`**: Флаги для управления поведением функции. - **`0`**: Нормальная отправка данных. - **`MSG_DONTROUTE`**: Не использовать маршрутизацию. - **`MSG_OOB`**: Отправка данных вне очереди (out-of-band). - **`dest_addr`**: Указатель на структуру `sockaddr`, содержащую адрес назначения. - **`addrlen`**: Размер структуры `sockaddr`. **Пример**: ```c const char *message = "Hello, server!"; struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); int bytes_sent = sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (bytes_sent < 0) { perror("sendto failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 4. `recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)` **Описание**: Получает данные через сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. - **`buf`**: Указатель на буфер для приема данных. - **`len`**: Максимальное количество байт для приема. - **`flags`**: Флаги для управления поведением функции. - **`0`**: Нормальный прием данных. - **`MSG_PEEK`**: Чтение данных без удаления из буфера. - **`MSG_WAITALL`**: Ждать получения всех данных до завершения вызова. - **`src_addr`**: Указатель на структуру `sockaddr`, куда будет записана информация об адресе источника. - **`addrlen`**: Указатель на переменную, содержащую размер структуры `sockaddr`. Функция изменяет эту переменную на фактический размер записи. **Пример**: ```c char buffer[1024]; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int bytes_received = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client_addr, &client_len); if (bytes_received < 0) { perror("recvfrom failed"); close(sockfd); exit(EXIT_FAILURE); } else { buffer[bytes_received] = '\0'; printf("Received message: %s from %s:%d\n", buffer, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); } ``` ### 5. `close(int sockfd)` **Описание**: Закрывает сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. **Пример**: ```c close(sockfd); ``` ### Пример сервера на UDP ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int sockfd; struct sockaddr_in servaddr, cliaddr; char buffer[BUFFER_SIZE]; // Создаем сокет if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } // Настройка адреса memset(&servaddr, 0, sizeof(servaddr)); memset(&cliaddr, 0, sizeof(cliaddr)); servaddr.sin_family = AF_INET; // IPv4 servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port = htons(PORT); // Привязываем сокет к адресу if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("bind failed"); close(sockfd); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); // Получаем сообщение от клиента int len, n; len = sizeof(cliaddr); while (1) { n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len); buffer[n] = '\0'; printf("Received message: %s\n", buffer); // Отправляем ответ клиенту sendto(sockfd, (const char *)buffer, strlen(buffer), MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len); printf("Echoed back to client\n"); } close(sockfd); return 0; } ``` ### Пример клиента на UDP ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define SERVER_PORT 8080 #define BUFFER_SIZE 1024 int main() { int sockfd; struct sockaddr_in servaddr; // Создаем сокет if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } // Настройка адреса сервера memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERVER_PORT); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Отправляем сообщение серверу const char *message = "Hello, server!"; sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr)); printf("Message sent to server\n"); // Получаем ответ от сервера char buffer[BUFFER_SIZE]; int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, NULL, NULL); buffer[n] = '\0'; printf("Received response: %s\n", buffer); // Закрываем сокет close(sockfd); return 0; } ``` ### Полезные советы 1. **Обработка ошибок**: Всегда проверяйте результаты системных вызовов и обрабатывайте ошибки. Например, используйте `perror()` для вывода сообщений об ошибках. 2. **Буферизация**: Используйте буферы для временного хранения данных при отправке и приеме. 3. **Порт и адрес**: Убедитесь, что порты и IP-адреса правильно заданы и доступны. 4. **Многопоточность**: Для обработки нескольких клиентов одновременно можно использовать многопоточность или асинхронные вызовы. ### Заключение Работа с UDP-сокетами включает использование функций `socket()`, `bind()`, `sendto()`, `recvfrom()` и `close()`. Эти функции позволяют создавать сокеты, привязывать их к адресам, отправлять и получать данные, а также закрывать сокеты после завершения работы. Начиная с простых примеров и постепенно усложняя задачи, вы сможете освоить все аспекты работы с UDP-сокетами.