Сокеты - это конечная точка сетевого общения между программами. Управление сокетами идет через стандартные файловые дескрипторы (как и весь I/O в Unix - это файлы).
Файловый дескриптор - это просто целочисленое значение, связанное с открытым файлом.
Файл в Unix может быть чем угодно: сетевым соединением, терминалом, файлом на диске и так далее.
Всё в Unix - это файл!
Поэтому, когда выхотите связи с другой программой по сети, вам потребуется работать с файловым дескриптором.
Чтобы получить файловый дескриптор для сокета, нужно вопспользоваться системной функцией - socket().
Затем, используя этот дескриптор, можно использовать системные функции для отправки/получения данны- send(), recv().
Функции
send()иrecv()
Это альтерантивы использования стандартных функций для работы с вводом/выводом файлов, таких какread()иwrite().
Особенность использованияsend()иrecv()в том, что
они дают больше контроля при передаче данных, что очень важно
для сетевых операций.
Существует два распространенных протокола в TCP/IP стэке - TCP и UDP.
TCP - является "протоколом ориентированным на соединение" (connection-oriented), с двусторонней связью. Он гарантирует цельную и упорядоченную передачу без ошибок.
TCP используется в большинстве приложений, где идет пошаговое взаимодействие и важны гарантии доставки пакетов.
Например:
- Протокол HTTP/HTTPS
- SSH
- передача файлов (FTP)
- базы данных
- почта
UDP - является "протоколом без соединения" (connectionless), с односторонней связью.
Конечно, соединение на самом деле есть, когда речь идет о доставке пакетов от пира А к пиру Б.
"Без соединения" означает, что каждый пакет отправленный UDP не требует подключения, пакет может быть доставлен, а может и нет. Протокол не гарантирует доставку и упорядоченную передачу. По аналогии с отправкой поздравительных писем через бумажную почту - вы не знаете дойдет ваше письмо или нет, и в каком порядке оно придет.
Поэтому UDP не имеет издержек, связанных с обеспечением гарантии доставки и упорядоченности и работает быстрее. Это делает UDP идельным протоколом для разных realtime-приложений, где важна минимальная задержка:
- сетевые игры в реальном времени
- видеостриминг
- аудиостриминг
- DNS
Сокет для TCP - это "Stream Socket", объявлен в константе SOCK_STREAM.
Сокет для UDP - это "Datagram Socket", объявлен в константе SOCK_DGRAM.
Файловый дескриптор- обычное целое число:
int
struct addrinfo используется для подготовки структур адреса сокета для последующего использования:
struct addrinfo { int ai_flags; // AI_PASSIVE, AI_CANONNAME, итд. int ai_family; // AF_INET, AF_INET6, AF_UNSPEC int ai_socktype; // SOCK_STREAM, SOCK_DGRAM int ai_protocol; // используется 0 для "любого" size_t ai_addrlen; // размер ai_addr в байтах struct sockaddr* ai_addr; // адрес- sockaddr_in или _in6 char* ai_canonname; // полное имя хоста struct addrinfo* ai_next; // следующий узел (это связный список) };
struct sockaddr содержит адресную информацию сокета:
struct sockaddr { unsigned short sa_family; // тип адреса (IPv4/IPv6) AF_INET, AF_INET6) char sa_data[14]; //14 байтов адреса протокола };
Чтобы удобнее работать со структурой struct sockaddr, программисты создали параллельную структуру: struct sockaddr_in (in - значит internet).
Важная деталь - указатели на
struct sockaddrиstruct sockaddr_inмогут приводится к типу друг-друга! И использоваться в системных функциях взаимозаменяемо!
// Это для IPv4, для IPv6 - sockaddr_in6 struct sockaddr_in { short int sin_family; //AF_INET/AF_INET6 unsigned short int sin_port; // Номер порта struct in_addr sin_addr; // Интернет-адрес unsigned char sin_zero[8]; //Такой же размер как struct sockaddr };
Обратите внимание на поле sin_zero[8], которое используется для выравнивания размера структуры со структурой struct sockaddr. Это поле должно быть обнулено с помощью системной функции memset().
Структура struct in_addr просто хранит в себе байты адреса. Для IPv4 - struct in_addr, для IPv6 - struct in_addr6.
// Версия для IPv4 struct in_addr { uint32_t s_addr; //32-битный int (4 байта) }
struct sockaddr_storage - хранилище для IPv4 и IPv6 адресов, достаточно ёмкое, чтобы поддерживать обе версии.
struct sockaddr_storage { sa_family_t ss_family; // семейство адреса (IPv4 / IPv6) //Остальное набивка памяти (padding), игнорируем // .... }