Введение
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 Descriptionsys/socket. h Core socket functions and data structures. netinet/in. h AF INET and AF INET6 address families and their corresponding protocol families, PF INET and PF INET6. These include standard IP addresses and TCP and UDP port numbers. sys/un. h PF UNIX and PF LOCAL address family. Used for local communication between programs running on the same computer. arpa/inet. h Functions for manipulating numeric IP addresses. netdb. h Functions for translating protocol names and host names into numeric addresses. Searches local data as well as name services.
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 – Функции для преобразования имен протоколов и хостов в числовые адреса. Выполняет поиск в локальных данных, а также использует службы имен.
File Descriptionsys/socket. h Core socket functions and data structures. netinet/in. h AF INET and AF INET6 address families and their corresponding protocol families, PF INET and PF INET6. These include standard IP addresses and TCP and UDP port numbers. sys/un. h PF UNIX and PF LOCAL address family. Used for local communication between programs running on the same computer. arpa/inet. h Functions for manipulating numeric IP addresses. netdb. h Functions for translating protocol names and host names into numeric addresses. Searches local data as well as name services.
Послушай.
После того, как сокет был связан с адресом, функция listen подготавливает его к приему входящих соединений. Однако это необходимо только для потоко-ориентированных (connection-oriented) режимов передачи данных, то есть для типов сокетов (SOCK_STREAM, SOCK_SEQPACKET). Функция listen требует два аргумента: sockfd – действительный дескриптор сокета; backlog – целое число, представляющее максимальное количество ожидающих соединений, которые могут быть поставлены в очередь. Операционная система обычно устанавливает ограничение на это значение. После принятия соединения, оно удаляется из очереди. В случае успеха возвращается 0. В случае ошибки возвращается -1.
sockfd, a valid socket descriptor. backlog, an integer representing the number of pending connections that can be queued up at any one time. The operating system usually places a cap on this value. Once a connection is accepted, it is dequeued. On success, 0 is returned. If an error occurs, 1 is returned.
принять
Когда приложение прослушивает потоковые соединения от других хостов, оно уведомляется о таких событиях (например, как в функции select) и должно инициализировать соединение, используя функцию accept. Она создает новый сокет для каждого соединения и удаляет соединение из очереди прослушивания. Функция имеет следующие аргументы: sockfd – дескриптор сокета прослушивания, в очереди которого находится соединение; cliaddr – указатель на структуру sockaddr для получения адресной информации клиента; addrlen – указатель на переменную типа socklen_t, указывающую размер структуры адреса клиента, переданной в accept. При возврате accept, эта переменная содержит размер (в байтах) структуры. Функция accept возвращает новый дескриптор сокета для принятого соединения или значение -1 в случае ошибки. Вся дальнейшая связь с удаленным хостом теперь осуществляется через этот новый сокет. Для датаграммных сокетов обработка функцией accept не требуется, поскольку получатель может немедленно ответить на запрос, используя сокет прослушивания.
sockfd, the descriptor of the listening socket that has the connection queued. cliaddr, a pointer to a sockaddr structure to receive the client's address information. addrlen, a pointer to a socklen t location that specifies the size of the client address structure passed to accept When accept returns, this location contains the size (in bytes) of the structure. accept returns the new socket descriptor for the accepted connection, or the value 1 if an error occurs. All further communication with the remote host now occurs via this new socket. Datagram sockets do not require processing by accept since the receiver may immediately respond to the request using the listening socket.
соединить
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 лишь одним из множества вариантов поиска, используемых этими функциями, и его использование может быть отключено и сегодня.
name specifies the name of the host. addr specifies a pointer to a struct in addr containing the address of the host. len specifies the length, in bytes, of addr. type specifies the address family type (e. g., AF INET) of the host address. The functions return a NULL pointer in case of error, in which case the external integer may be checked to see whether this is a temporary failure or an invalid or unknown host. Otherwise a valid struct hostent * is returned. These functions are not strictly a component of the BSD socket API, but are often used in conjunction with the API functions for looking up a host. These functions are now considered legacy interfaces for querying the domain name system. New functions that are completely protocol agnostic (supporting IPv6) have been defined. These new functions are getaddrinfo and getnameinfo , and are based on a new addrinfo data structure. This pair of functions appeared at the same time as the BSD socket API proper in 4.2BSD (1983), the same year DNS was first created. Early versions did not query DNS and only performed /etc/hosts lookup. The 4.3BSD (1984) version added DNS in a crude way. The current implementation using Name Service Switch derives Solaris and later NetBSD 1.4 (1999). Initially defined for NIS+, NSS makes DNS only one of the many options for lookup by these functions and its use can be disabled even today.
Необработанные розетки
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.