/
bvs22
/
draft
Обзор
Документация
Войти
/
bvs22
/
draft
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
network/socket
461 строка
17 KB
Batomunkuev Vladimir
1
01 фев 2025, 08:19
01 фев 2025, 08:19
ed6e22e
Код
Авторство
О чём код?
### Конспект по сокетам на языке Си для начинающих #### Введение Сокеты (sockets) — это абстракция, используемая для сетевого взаимодействия между процессами. Они позволяют программам отправлять и получать данные через сети, будь то локальная сеть или Интернет. В языке программирования C сокеты реализуются через библиотеку сокетов (socket library), которая предоставляет функции для создания и управления соединениями. #### Основные концепции 1. **Сокет**: Это конечная точка в сети, через которую происходит передача данных. 2. **Адресация**: Для уникальной идентификации сокета используется IP-адрес и порт. 3. **Протоколы**: - **TCP (Transmission Control Protocol)**: Надежный протокол, обеспечивающий доставку данных в правильном порядке. - **UDP (User Datagram Protocol)**: Быстрый протокол, но не гарантирующий доставки данных. #### Шаги для работы с сокетами 1. **Инициализация сокета**: - Создание сокета с помощью функции `socket()`. - Установка адреса сокета с помощью функции `bind()`. - Установление соединения с удаленным хостом (для TCP) с помощью функции `connect()`. 2. **Отправка и получение данных**: - Отправка данных с помощью функции `send()` или `sendto()`. - Получение данных с помощью функции `recv()` или `recvfrom()`. 3. **Закрытие сокета**: - Закрытие сокета с помощью функции `close()`. #### Примеры кода ##### Пример 1: Сервер TCP ```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 server_fd, new_socket; struct sockaddr_in address; int addrlen = sizeof(address); char buffer[BUFFER_SIZE] = {0}; const char *hello = "Hello from server"; // Создаем сокет if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // Настройка адреса address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // Привязываем сокет к адресу if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } // Переходим в режим прослушивания if (listen(server_fd, 3) < 0) { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); // Принимаем входящее соединение if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept failed"); close(server_fd); exit(EXIT_FAILURE); } // Читаем данные от клиента read(new_socket, buffer, BUFFER_SIZE); printf("Received message: %s\n", buffer); // Отправляем ответ клиенту send(new_socket, hello, strlen(hello), 0); printf("Hello message sent\n"); // Закрываем сокеты close(new_socket); close(server_fd); return 0; } ``` ##### Пример 2: Клиент TCP ```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 sock = 0; struct sockaddr_in serv_addr; char buffer[BUFFER_SIZE] = {0}; const char *hello = "Hello from client"; // Создаем сокет if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return -1; } // Настройка адреса сервера serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // Преобразуем IP-адрес из текстового формата в двоичный if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported \n"); return -1; } // Устанавливаем соединение с сервером if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection Failed \n"); return -1; } // Отправляем сообщение серверу send(sock, hello, strlen(hello), 0); printf("Hello message sent\n"); // Читаем ответ от сервера read(sock, buffer, BUFFER_SIZE); printf("Received message: %s\n", buffer); // Закрываем сокет close(sock); return 0; } ``` ##### Пример 3: Сервер 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; int len; char buffer[BUFFER_SIZE]; const char *hello = "Hello from UDP server"; int n; // Создаем сокет 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); } len = sizeof(cliaddr); // Размер структуры адреса клиента // Получаем сообщение от клиента 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 *)hello, strlen(hello), MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len); printf("Hello message sent\n"); // Закрываем сокет close(sockfd); return 0; } ``` ##### Пример 4: Клиент 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; const char *hello = "Hello from UDP client"; 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)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Отправляем сообщение серверу sendto(sockfd, (const char *)hello, strlen(hello), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr)); printf("Hello message sent\n"); // Читаем ответ от сервера int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) NULL, NULL); buffer[n] = '\0'; printf("Received message: %s\n", buffer); // Закрываем сокет close(sockfd); return 0; } ``` #### Основные функции Давайте рассмотрим основные функции работы с сокетами более подробно, включая их аргументы и флаги. ### 1. `socket(int domain, int type, int protocol)` **Описание**: Создает новый сокет. **Аргументы**: - **`domain`**: Адресное пространство (или семейство протоколов). - **`AF_INET`**: IPv4. - **`AF_INET6`**: IPv6. - **`AF_UNIX`**: Локальные межпроцессорные коммуникации (Unix доменные сокеты). - **`type`**: Тип сокета. - **`SOCK_STREAM`**: Потоковый сокет (используется для TCP). - **`SOCK_DGRAM`**: Датаграммный сокет (используется для UDP). - **`SOCK_RAW`**: Сырой сокет (для низкоуровневых операций). - **`protocol`**: Протокол, который будет использоваться с этим сокетом. - Обычно используется значение `0`, что означает использование стандартного протокола для данного типа сокета (например, TCP для `SOCK_STREAM` и UDP для `SOCK_DGRAM`). **Пример**: ```c int sockfd = socket(AF_INET, SOCK_STREAM, 0); ``` ### 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. `listen(int sockfd, int backlog)` **Описание**: Переводит сокет в режим прослушивания для входящих соединений. **Аргументы**: - **`sockfd`**: Дескриптор сокета, созданный функцией `socket()` и привязанный функцией `bind()`. - **`backlog`**: Максимальное количество ожидающих соединений. Если очередь соединений достигнет этого значения, новые соединения будут отклонены. **Пример**: ```c if (listen(sockfd, 3) < 0) { perror("listen failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 4. `accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)` **Описание**: Принимает входящее соединение и создает новый сокет для общения с клиентом. **Аргументы**: - **`sockfd`**: Дескриптор сокета, созданный функцией `socket()` и переведенный в режим прослушивания функцией `listen()`. - **`addr`**: Указатель на структуру `sockaddr`, куда будет записана информация об адресе клиента. - **`addrlen`**: Указатель на переменную, содержащую размер структуры `sockaddr`. Функция изменяет эту переменную на фактический размер записи. **Пример**: ```c struct sockaddr_in client_addr; int client_len = sizeof(client_addr); int new_socket = accept(sockfd, (struct sockaddr *)&client_addr, &client_len); if (new_socket < 0) { perror("accept failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 5. `connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)` **Описание**: Устанавливает соединение с удаленным хостом. **Аргументы**: - **`sockfd`**: Дескриптор сокета, созданный функцией `socket()`. - **`addr`**: Указатель на структуру `sockaddr`, содержащую информацию об адресе и порте сервера. - **`addrlen`**: Размер структуры `sockaddr`. **Пример**: ```c struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr); if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("connect failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 6. `send(int sockfd, const void *buf, size_t len, int flags)` **Описание**: Отправляет данные через сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. - **`buf`**: Указатель на буфер с данными для отправки. - **`len`**: Количество байт для отправки. - **`flags`**: Флаги для управления поведением функции. - **`0`**: Нормальная отправка данных. - **`MSG_DONTROUTE`**: Не использовать маршрутизацию. - **`MSG_OOB`**: Отправка данных вне очереди (out-of-band). **Пример**: ```c const char *message = "Hello, server!"; int bytes_sent = send(sockfd, message, strlen(message), 0); if (bytes_sent < 0) { perror("send failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ### 7. `recv(int sockfd, void *buf, size_t len, int flags)` **Описание**: Получает данные через сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. - **`buf`**: Указатель на буфер для приема данных. - **`len`**: Максимальное количество байт для приема. - **`flags`**: Флаги для управления поведением функции. - **`0`**: Нормальный прием данных. - **`MSG_PEEK`**: Чтение данных без удаления из буфера. - **`MSG_WAITALL`**: Ждать получения всех данных до завершения вызова. **Пример**: ```c char buffer[1024]; int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0); if (bytes_received < 0) { perror("recv failed"); close(sockfd); exit(EXIT_FAILURE); } else if (bytes_received == 0) { printf("Connection closed by peer\n"); } else { buffer[bytes_received] = '\0'; printf("Received message: %s\n", buffer); } ``` ### 8. `close(int sockfd)` **Описание**: Закрывает сокет. **Аргументы**: - **`sockfd`**: Дескриптор сокета. **Пример**: ```c close(sockfd); ``` #### Полезные советы 1. **Обработка ошибок**: Всегда проверяйте результаты системных вызовов и обрабатывайте ошибки. 2. **Буферизация**: Используйте буферы для временного хранения данных при отправке и приеме. 3. **Порт и адрес**: Убедитесь, что порты и IP-адреса правильно заданы и доступны. 4. **Многопоточность**: Для обработки нескольких клиентов одновременно можно использовать многопоточность или асинхронные вызовы. #### Заключение Сокеты — мощный инструмент для сетевого программирования на языке C. Понимание основных концепций и умение работать с ключевыми функциями позволит вам создавать сложные сетевые приложения. Начиная с простых примеров и постепенно усложняя задачи, вы сможете освоить все аспекты работы с сокетами.