Введение

API для межпроцессного взаимодействия

Berkeley sockets – это интерфейс программирования приложений (API) для интернет-сокетов и сокетов доменов Unix, используемый для межпроцессного взаимодействия (IPC). Обычно он реализуется в виде библиотеки подключаемых модулей. Он появился вместе с операционной системой 4.2BSD Unix, выпущенной в 1983 году. Сокет – это абстрактное представление (дескриптор) локальной конечной точки пути сетевого взаимодействия. API Berkeley sockets представляет его как дескриптор файла в философии Unix, обеспечивающий общий интерфейс для ввода и вывода потоков данных. Сокеты Berkeley эволюционировали с незначительными изменениями из де-факто стандарта в компонент спецификации POSIX. Термин POSIX-сокеты по сути является синонимом сокетов Berkeley, но они также известны как BSD-сокеты, в знак признания первой реализации в Berkeley Software Distribution.

История и реализация

Сокеты Беркли появились вместе с операционной системой 4.2BSD Unix, выпущенной в 1983 году, как программный интерфейс. Однако Калифорнийский университет в Беркли смог выпустить версии операционной системы и сетевой библиотеки, свободные от лицензионных ограничений проприетарной Unix корпорации AT&T, лишь в 1989 году. Все современные операционные системы реализуют ту или иную версию интерфейса сокетов Беркли. Он стал стандартным интерфейсом для приложений, работающих в сети Интернет. Даже реализация Winsock для MS Windows, созданная независимыми разработчиками, тесно следует стандарту. API BSD sockets написан на языке программирования C. Большинство других языков программирования предоставляют аналогичные интерфейсы, как правило, реализованные в виде библиотеки-обертки, основанной на C API.

Альтернативы

API на основе STREAMS Transport Layer Interface (TLI) является альтернативой API сокетов. Многие системы, предоставляющие TLI API, также предоставляют и Berkeley socket API. Не-Unix системы часто предоставляют Berkeley socket API с уровнем трансляции в собственный сетевой API. Plan 9 и Genode используют файловые API с управляющими файлами вместо файловых дескрипторов.

Файлы заголовка

Интерфейс сокета Berkeley определяется в нескольких файлах заголовков. Названия и содержимое этих файлов незначительно различаются в зависимости от реализации. В общем случае они включают:

File Descriptions
sys/socket.h – Основные функции и структуры данных сокетов.
netinet/in.h – Адресные семейства AF_INET и AF_INET6 и соответствующие им семейства протоколов PF_INET и PF_INET6. Включают стандартные IP-адреса и номера портов TCP и UDP.
sys/un.h – Адресные семейства PF_UNIX и PF_LOCAL. Используется для локальной связи между программами, работающими на одном компьютере.
arpa/inet.h – Функции для работы с числовыми IP-адресами.
netdb.h – Функции для преобразования имен протоколов и хостов в числовые адреса. Выполняет поиск в локальных данных, а также использует службы имен.

Послушай.

После того, как сокет был связан с адресом, функция listen подготавливает его к приему входящих соединений. Однако это необходимо только для потоко-ориентированных (connection-oriented) режимов передачи данных, то есть для типов сокетов (SOCK_STREAM, SOCK_SEQPACKET). Функция listen требует два аргумента: sockfd – действительный дескриптор сокета; backlog – целое число, представляющее максимальное количество ожидающих соединений, которые могут быть поставлены в очередь. Операционная система обычно устанавливает ограничение на это значение. После принятия соединения, оно удаляется из очереди. В случае успеха возвращается 0. В случае ошибки возвращается -1.

принять

Когда приложение прослушивает потоковые соединения от других хостов, оно уведомляется о таких событиях (например, как в функции select) и должно инициализировать соединение, используя функцию accept. Она создает новый сокет для каждого соединения и удаляет соединение из очереди прослушивания. Функция имеет следующие аргументы: sockfd – дескриптор сокета прослушивания, в очереди которого находится соединение; cliaddr – указатель на структуру sockaddr для получения адресной информации клиента; addrlen – указатель на переменную типа socklen_t, указывающую размер структуры адреса клиента, переданной в accept. При возврате accept, эта переменная содержит размер (в байтах) структуры. Функция accept возвращает новый дескриптор сокета для принятого соединения или значение -1 в случае ошибки. Вся дальнейшая связь с удаленным хостом теперь осуществляется через этот новый сокет. Для датаграммных сокетов обработка функцией accept не требуется, поскольку получатель может немедленно ответить на запрос, используя сокет прослушивания.

соединить

connect устанавливает прямое коммуникационное соединение с конкретным удаленным хостом, идентифицированным его адресом, через сокет, идентифицированный его файловым дескриптором. При использовании протокола с установлением соединения это устанавливает соединение. Определенные типы протоколов являются без установления соединения, наиболее заметным из которых является протокол пользовательских дейтаграмм (UDP). При использовании с протоколами без установления соединения, connect определяет удаленный адрес для отправки и приема данных, позволяя использовать функции, такие как send и recv. В этих случаях функция connect предотвращает прием дейтаграмм от других источников. connect возвращает целое число, представляющее код ошибки: 0 означает успех, а -1 – ошибку. Исторически, в системах, основанных на BSD, состояние файлового дескриптора сокета не определено, если вызов connect завершается неудачно (как указано в Single Unix Specification), поэтому переносимые приложения должны немедленно закрыть файловый дескриптор сокета и получить новый дескриптор с помощью socket, в случае неудачи вызова connect.

gethostbyname и gethostbyaddr

Функции `gethostbyname` и `gethostbyaddr` используются для разрешения имен и адресов хостов в системе доменных имен или других механизмах разрешения локального хоста (например, поиск в файле /etc/hosts). Они возвращают указатель на объект типа `struct hostent`, который описывает хост протокола Интернет. Функции принимают следующие аргументы: `name` указывает имя хоста, `addr` указывает на структуру, содержащую адрес хоста, `len` указывает длину в байтах структуры `addr`, а `type` указывает тип семейства адресов (например, `AF_INET`) хост-адреса. В случае ошибки функции возвращают `NULL`-указатель, при этом внешняя целочисленная переменная может быть проверена для определения, является ли ошибка временной или связана с недействительным или неизвестным хостом. В противном случае возвращается валидный указатель `struct hostent*`. Эти функции не являются строгим компонентом BSD socket API, но часто используются совместно с функциями API для поиска хоста. В настоящее время эти функции считаются устаревшими интерфейсами для запроса системы доменных имен. Определены новые функции, полностью независимые от протокола (поддерживающие IPv6) – `getaddrinfo` и `getnameinfo`, основанные на новой структуре данных `addrinfo`. Эта пара функций появилась одновременно с BSD socket API в 4.2BSD (1983), в тот же год, когда был впервые создан DNS. Первоначальные версии не выполняли запросы к DNS и осуществляли только поиск в файле /etc/hosts. В версии 4.3BSD (1984) DNS был добавлен в упрощенном виде. Современная реализация, использующая Name Service Switch, берет свое начало от Solaris и более поздних версий NetBSD 1.4 (1999). Изначально разработанный для NIS+, NSS делает DNS лишь одним из множества вариантов поиска, используемых этими функциями, и его использование может быть отключено и сегодня.

Необработанные розетки

Raw-сокеты предоставляют простой интерфейс, обходящий обработку стеком TCP/IP хоста. Они позволяют реализовывать сетевые протоколы в пользовательском пространстве и облегчают отладку стека протоколов. Некоторые службы, такие как ICMP, использующие сетевой уровень модели TCP/IP, применяют raw-сокеты.

Режим блокировки и не блокировки

Сокеты Беркли могут работать в одном из двух режимов: блокирующем или неблокирующем. Блокирующий сокет не возвращает управление, пока не отправит (или не получит) часть или все данные, указанные для операции. Для блокирующего сокета нормально не отправить все данные сразу. Приложение должно проверять возвращаемое значение, чтобы определить, сколько байтов было отправлено или получено, и повторно отправлять любые данные, которые еще не были обработаны. При использовании блокирующих сокетов следует уделять особое внимание функции accept, так как она может продолжать блокироваться даже после сигнала о готовности к чтению, если клиент отключается на этапе установления соединения. Неблокирующий сокет возвращает данные, находящиеся в буфере приема, и немедленно продолжает выполнение. Программы, использующие неблокирующие сокеты, особенно подвержены гонкам данных из-за колебаний скорости сетевого соединения, если они написаны некорректно. Переключение сокета в блокирующий или неблокирующий режим обычно осуществляется с помощью функций fcntl и ioctl.

Заканчивающие розетки

Операционная система не освобождает ресурсы, выделенные сокету, пока сокет не будет закрыт. Это особенно важно, если вызов connect завершился неудачей и будет предпринята повторная попытка. Когда приложение закрывает сокет, уничтожается только интерфейс к сокету. Ответственность за внутреннее уничтожение сокета лежит на ядре. Иногда сокет может находиться в определенном состоянии на стороне сервера до 4 минут. В системах SVR4 использование может привести к потере данных. Для гарантии доставки всех данных в этих системах может потребоваться использование опции SO_LINGER.

Пример клиент-сервер с использованием TCP

Протокол управления передачей (TCP) — это протокол, ориентированный на установление соединения, который предоставляет разнообразные функции коррекции ошибок и повышения производительности для передачи потоков байтов. Процесс создает TCP-сокет, вызывая функцию с параметрами семейства протоколов, режимом сокета для потоковых сокетов и идентификатором протокола IP для TCP.

Пример клиент-сервер с использованием UDP

Протокол пользовательских дейтаграмм (UDP) — это протокол без установления соединения, не гарантирующий доставку. Пакеты UDP могут приходить не по порядку, повторяться или вообще не доходить. Благодаря этой минималистичной конструкции, UDP имеет значительно меньшие накладные расходы, чем TCP. Отсутствие установления соединения означает, что не существует понятия потока или постоянного соединения между двумя хостами. Такие данные называются дейтаграммами (дейтаграммными сокетами). Адресное пространство UDP, пространство номеров портов UDP (в терминологии ISO — TSAP), полностью отделено от адресного пространства портов TCP.