Skip to content

Latest commit

 

History

History
137 lines (96 loc) · 7.44 KB

File metadata and controls

137 lines (96 loc) · 7.44 KB

Сокеты

Что такое сокеты?

Сокеты - это конечная точка сетевого общения между программами. Управление сокетами идет через стандартные файловые дескрипторы (как и весь I/O в Unix - это файлы).

Файловый дескриптор - это просто целочисленое значение, связанное с открытым файлом.

Файл в Unix может быть чем угодно: сетевым соединением, терминалом, файлом на диске и так далее.

Всё в Unix - это файл!

Поэтому, когда выхотите связи с другой программой по сети, вам потребуется работать с файловым дескриптором.

Чтобы получить файловый дескриптор для сокета, нужно вопспользоваться системной функцией - socket().

Затем, используя этот дескриптор, можно использовать системные функции для отправки/получения данны- send(), recv().

Функции send() и recv()
Это альтерантивы использования стандартных функций для работы с вводом/выводом файлов, таких как read() и write().
Особенность использования send() и recv() в том, что
они дают больше контроля при передаче данных, что очень важно
для сетевых операций.

Два типа сокетов

Существует два распространенных протокола в TCP/IP стэке - TCP и UDP.

Протокол TCP

TCP - является "протоколом ориентированным на соединение" (connection-oriented), с двусторонней связью. Он гарантирует цельную и упорядоченную передачу без ошибок.

TCP используется в большинстве приложений, где идет пошаговое взаимодействие и важны гарантии доставки пакетов.

Например:

  • Протокол HTTP/HTTPS
  • SSH
  • передача файлов (FTP)
  • базы данных
  • почта

Протокол UDP

UDP - является "протоколом без соединения" (connectionless), с односторонней связью.

Конечно, соединение на самом деле есть, когда речь идет о доставке пакетов от пира А к пиру Б.

"Без соединения" означает, что каждый пакет отправленный UDP не требует подключения, пакет может быть доставлен, а может и нет. Протокол не гарантирует доставку и упорядоченную передачу. По аналогии с отправкой поздравительных писем через бумажную почту - вы не знаете дойдет ваше письмо или нет, и в каком порядке оно придет.

Поэтому UDP не имеет издержек, связанных с обеспечением гарантии доставки и упорядоченности и работает быстрее. Это делает UDP идельным протоколом для разных realtime-приложений, где важна минимальная задержка:

  • сетевые игры в реальном времени
  • видеостриминг
  • аудиостриминг
  • DNS

Сокеты под TCP и UDP

Сокет для 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), игнорируем
  // ....
}