Сетевые средства
Стандарт POSIX-2001 определяет сеть как совокупность взаимосвязанных хостов. Под сетевым адресом понимается видимый в пределах сети идентификатор, используемый для обозначения оконечных точек сети.
Процесс присвоения сетевого адреса оконечной точке называется связыванием, или привязкой, а обратное действие - освобождением , или отменой привязки.
Обычно оконечной точкой служит аппаратный сетевой интерфейс, посредством которого данные передаются и принимаются, однако с таким интерфейсом, как шлейфовый (loopback), никакой аппаратуры не ассоциировано.
Данные передаются по сети в виде последовательности октетов (восьмибитных беззнаковых величин). Если некоторый элемент данных (например, адрес или номер порта) состоит более чем из восьми бит, для его передачи и хранения требуется несколько октетов. Сетевым называется порядок октетов (байт), при котором первый (с наименьшим адресом) октет содержит старшие (наиболее значимые) биты.
При взаимодействии процессов оконечными точками служат сокеты, стандарт POSIX-2001 трактует их как отдельный тип файлов.
Под адресом сокета как (удаленной) оконечной точки понимается структура, включающая идентификатор адресного семейства и специфичную для данного семейства адресную информацию.
Адресное семейство соответствует определенной среде взаимодействия. Стандарт POSIX-2001 определяет три таких семейства: AF_UNIX (межпроцессное взаимодействие в пределах одной системы), AF_INET (взаимодействие по протоколам IPv4), AF_INET6 (взаимодействие по протоколам IPv6 - необязательная возможность).
В пределах каждого адресного семейства могут существовать сокеты нескольких типов. Стандартом POSIX-2001 предусмотрено четыре типа: SOCK_STREAM (надежные, упорядоченные, полнодуплексные потоки октетов в режиме с установлением соединения), SOCK_SEQPACKET (аналог SOCK_STREAM с дополнительным сохранением границ между записями), SOCK_DGRAM (передача данных в виде датаграмм в режиме без установления соединения), SOCK_RAW (аналог SOCK_DGRAM с дополнительной возможностью доступа к протокольным заголовкам и другой информации нижнего уровня - необязательная возможность).
Для каждого адресного семейства каждый тип сокета может поддерживаться одним или несколькими протоколами. В частности, в адресном семействе AF_INET для сокетов типа SOCK_STREAM подразумеваемым является протокол с именем IPPROTO_TCP, а для типа SOCK_DGRAM - IPPROTO_UDP.
Общая логика работы с сокетами состоит в следующем. Сокеты создаются с помощью функции socket(), которой в качестве аргументов передаются адресное семейство, тип сокета и протокол, а в качестве результата получают открытый файловый дескриптор. Затем, посредством функции bind(), сокету присваивают локальный адрес. Если сокет ориентирован на режим с установлением соединения, то его следует пометить как готового принимать соединения, для чего понадобится функция listen(). Реальный прием соединений выполняет функция accept(), создающая для каждого из них новый сокет по образу и подобию "слушающего". В свою очередь, потенциальный партнер по взаимодействию инициирует соединение, прибегнув к функции connect(). (в режиме без установления соединения функция connect() позволяет специфицировать адрес отправляемых через сокет датаграмм.)
Для приема данных, поступивших в сокет, можно воспользоваться универсальной функцией низкоуровневого ввода/вывода read() или специализированным семейством функций recv*(), а для передачи - функцией write() или семейством send*(). Кроме того, посредством функций select() и/или poll() можно опросить наличие данных для приема или возможность отправки очередной порции данных.
Завершается взаимодействие между партнерами обращением к функции shutdown().
Данные о хостах как узлах сети хранятся в сетевой базе, последовательный доступ к которой обслуживается функциями sethostent(), gethostent() и endhostent().
Функция getaddrinfo() позволяет по имени узла сети (хоста) и/или имени сетевого сервиса получить набор адресов сокетов и ассоциированную информацию, что дает возможность создать сокет для обращения к заданному сервису.
Функция freeaddrinfo() носит технический характер: благодаря ей можно освободить память, зарезервированную функцией getaddrinfo().
Функцию getnameinfo() можно считать обратной по отношению к getaddrinfo(). Она позволяет по адресу сокета узнать имя узла и сервиса.
Техническую роль играют и функции преобразования ip-адресов из текстового представления в числовое и наоборот: inet_addr(), inet_ntoa(), inet_pton(), inet_ntop().
Первые две манипулируют только адресами IPv4: inet_addr() преобразует текстовую цепочку в целочисленное значение, пригодное для использования в качестве ip-адреса, inet_ntoa() выполняет обратное преобразование.
Вторая пара функций по сути аналогична первой, но имеет чуть более общий характер, так как способна преобразовывать адреса в формате IPv6.
Для преобразования значений типов uint16_t и uint32_t из хостового порядка байт в сетевой служат функции htons() и htonl(); функции ntohs() и ntohl() осуществляют обратную операцию.
Полезным дополнением к функциям getaddrinfo() и getnameinfo() является функция gai_strerror(), возвращающая текстовую цепочку, которая расшифровывает коды ошибок, перечисленные в заголовочном файле <netdb.h>.
Наряду с базой данных хостов (узлов сети), поддерживается база данных сетей с аналогичной логикой работы и набором функций: setnetent(), getnetent(), getnetbyaddr(), getnetbyname(), endnetent().
Функция getnetent() обслуживает последовательный доступ к базе, getnetbyaddr() осуществляет поиск по адресному семейству и номеру сети, а getnetbyname() выбирает сеть с заданным (официальным) именем.
Точно такой же программный интерфейс предоставляет база данных сетевых протоколов: setprotoent(), getprotoent(), getprotobyname(), getprotobynumber(), endprotoent().
Еще одно проявление той же логики работы - база данных сетевых сервисов: setservent(), getservent(), getservbyname(), getservbyport(), endservent().
Для создания сокетов, помимо функции socket(), может быть использована функция socketpair(), создающая пару сокетов с установленным между ними соединением. Она обычно используется для адресного семейства AF_UNIX; поддержка других семейств не гарантируется.
Опросить присвоенный локальный адрес (его иногда называют именем сокета) можно с помощью функции getsockname().
С сокетами могут быть ассоциированы опции, влияющие на их функционирование. Опросить или изменить значения этих опций помогут функций getsockopt() и setsockopt().
Функция getpeername() позволяет опросить еще одну характеристику - адрес (имя) сокета, с которым установлено соединение.
После привязки сокета к локальному адресу и, возможно, установления соединения и задания значений опций, следует приступать к отправке и/или приему данных через сокет. Для этого служат функции recvfrom(), recv(), recvmsg(), sendto(), send(), sendmsg().