Сокеты Berkeley - это интерфейс прикладного программирования (API) для Интернет-сокетов и доменные сокеты Unix, используемые для межпроцессного взаимодействия (IPC). Обычно он реализуется как библиотека связываемых модулей. Он возник в операционной системе 4.2BSD Unix, выпущенной в 1983 году.
Сокет - это абстрактное представление (дескриптор ) для локальной конечной точки сетевого коммуникационного пути.. API сокетов Беркли представляет его как файловый дескриптор (дескриптор файла ) в философии Unix, который обеспечивает общий интерфейс для ввода и вывода в потоки. данных.
Сокеты Berkeley с небольшими изменениями превратились из стандарта де-факто в компонент спецификации POSIX. Термин сокеты POSIX по существу является синонимом сокетов Беркли, но они также известны как сокеты BSD, что свидетельствует о первой реализации в Распространении программного обеспечения Беркли.
Сокеты Беркли возникли в операционной системе 4.2BSD Unix , выпущенной в 1983 году, как интерфейс программирования. Однако только в 1989 году Калифорнийский университет в Беркли не смог выпустить версии операционной системы и сетевой библиотеки, свободные от лицензионных ограничений проприетарной Unix ATT Corporation.
Все современные операционные системы реализуют версию интерфейса сокетов Беркли. Он стал стандартным интерфейсом для приложений, работающих в Интернете. Даже реализация Winsock для MS Windows, созданная независимыми разработчиками, полностью соответствует стандарту.
API сокетов BSD написан на языке программирования C. Большинство других языков программирования предоставляют аналогичные интерфейсы, обычно написанные как библиотека-оболочка на основе C API.
По мере развития API сокетов Berkeley и в конечном итоге дало API сокетов POSIX, некоторые функции были объявлены устаревшими или удалены и заменены другими. POSIX API также разработан для повторного входа.
Action | BSD | POSIX |
---|---|---|
Преобразование из текстового адреса в упакованный адрес | inet_aton | inet_pton |
Преобразование упакованного адреса в текстовый адрес | inet_ntoa | inet_ntop |
Прямой поиск имени хоста / службы | gethostbyname, gethostbyaddr, getservbyname, getservbyport | getaddrinfo |
Обратный поиск имени хоста / службы | gethostbyaddr, getservbyport | getnameinfo |
На основе STREAMS Интерфейс транспортного уровня (TLI) API предлагает альтернативу API сокетов. Многие системы, которые предоставляют TLI API, также предоставляют API сокетов Berkeley.
Системы, отличные от Unix, часто предоставляют API сокетов Berkeley со слоем трансляции в собственный сетевой API. Plan 9 и Genode используют API файловой системы с управляющими файлами, а не с файловыми дескрипторами.
Интерфейс сокета Беркли определен в нескольких заголовочных файлах. Имена и содержимое этих файлов немного различаются между реализациями. Как правило, они включают:
Файл | Описание |
---|---|
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 | Функции для преобразования имен протоколов и имен хостов в числовые адреса. Ищет локальные данные, а также службы имен. |
API сокетов Berkeley обычно предоставляет следующие функции:
Функция socket () создает конечную точку для связи и возвращает файловый дескриптор для розетки. Он использует три аргумента:
Функция возвращает -1, если произошла ошибка. В противном случае она возвращает целое число, представляющее вновь назначенный дескриптор.
bind () связывает сокет с адрес. Когда socket создается с помощью socket (), ему присваивается только семейство протоколов, но не назначается адрес. Это соединение должно быть выполнено до того, как сокет сможет принимать соединения от других хостов. Функция имеет три аргумента:
Bind () возвращает 0 в случае успеха и -1 в случае ошибки.
После того, как сокет был связан с адресом, listen () подготавливает его для входящих соединений. Однако это необходимо только для потоковых (ориентированных на соединение) режимов данных, то есть для типов сокетов (SOCK_STREAM, SOCK_SEQPACKET). listen () требует двух аргументов:
Как только соединение принято, оно удаляется из очереди. В случае успеха возвращается 0. В случае ошибки возвращается -1.
Когда приложение прослушивает потоковые соединения от других хостов, оно уведомляется о таких событиях (см. select () функция) и должно инициализировать соединение с использованием функции accept (). Он создает новый сокет для каждого соединения и удаляет соединение из очереди прослушивания. Функция имеет следующие аргументы:
accept () возвращает новый дескриптор сокета для принятого соединения или значение -1, если возникает ошибка. Все дальнейшие коммуникации с удаленным хостом теперь происходят через этот новый сокет.
Сокеты дейтаграмм не требуют обработки с помощью accept (), поскольку получатель может немедленно ответить на запрос, используя прослушивающий сокет.
connect () устанавливает прямую связь с конкретным удаленным хостом, идентифицированным по его адресу, через сокет, идентифицированный его файловым дескриптором.
При использовании протокола с установлением соединения устанавливается соединение. Некоторые типы протоколов не требуют установления соединения, в первую очередь User Datagram Protocol. При использовании с протоколами без установления соединения connect определяет удаленный адрес для отправки и получения данных, позволяя использовать такие функции, как send и recv. В этих случаях функция подключения предотвращает прием дейтаграмм из других источников.
connect () возвращает целое число, представляющее код ошибки: 0 представляет успех, а –1 представляет ошибку. Исторически сложилось так, что в системах, производных от BSD, состояние дескриптора сокета не определено, если вызов для подключения терпит неудачу (как это указано в спецификации Single Unix), поэтому переносимые приложения должны немедленно закрыть дескриптор сокета и получить новый дескриптор с socket () в случае сбоя вызова connect ().
Функции gethostbyname () и gethostbyaddr () используются для разрешения имен и адресов хостов в система доменных имен или другие механизмы распознавателя локального хоста (например, поиск / etc / hosts). Они возвращают указатель на объект типа struct hostent, который описывает хост Internet Protocol. Функции используют следующие аргументы:
Функции возвращают нулевой указатель в в случае ошибки, и в этом случае внешнее целое число h_errnoможет быть проверено, чтобы увидеть, является ли это временной ошибкой или недопустимым или неизвестным хостом. В противном случае возвращается действительная структура struct hostent *.
Эти функции не являются строго компонентом API сокетов BSD, но часто используются вместе с функциями API. Более того, эти функции теперь считаются устаревшими интерфейсами для запросов к системе доменных имен. Были определены новые функции, которые полностью не зависят от протокола (поддерживают IPv6). Эти новые функции - это getaddrinfo () и getnameinfo (), и они основаны на новой структуре данных addrinfo.
API сокетов Berkeley - это общий интерфейс для сетевого и межпроцессного взаимодействия, который поддерживает использование различных сетевых протоколов и адресных архитектур.
Ниже приводится список семейств протоколов (которым предшествует стандартный символический идентификатор), определенных в современной реализации Linux или BSD :
Идентификатор | Функция или использование |
---|---|
PF_LOCAL, PF_UNIX, PF_FILE | Локальный для хоста (каналы и файловый домен) |
PF_INET | Интернет-протокол версии 4 |
PF_AX25 | Любительское радио AX.25 |
PF_IPX | Novell Межсетевой пакетный обмен |
PF_APPLETALK | AppleTalk |
PF_NETROM | Любительское радио NetROM (относящееся к AX.25) |
PF_BRIDGE | Многопротокольный мост |
PF_ATMPVC | Асинхронный режим передачи Постоянные виртуальные цепи |
PF_ATMSVC | Режим асинхронной передачи переключаемый виртуальный Цепи |
PF_INET6 | Интернет-протокол версии 6 |
PF_DECnet | Зарезервировано для проекта DECnet |
PF_NETBEUI | Зарезервировано для проекта 802.2LLC |
PF_SECURITY | Security callback pseudo AF |
PF_KEY | API управления ключами PF_KEY |
PF_NETLINK, PF_RO UTE | API маршрутизации |
PF_PACKET | Сокеты для захвата пакетов |
PF_ECONET | Acorn Econet |
PF_SNA | Linux Системная сетевая архитектура (SNA) Проект |
PF_IRDA | IrDA сокеты |
PF_PPPOX | PPP через X сокеты |
PF_WANPIPE | Sangoma Wanpipe API сокеты |
PF_BLUETOOTH | Bluetooth сокеты |
Сокет для связи создается с помощью функции socket ()
путем указания желаемого семейства протоколов (PF_-identifier) как Аргумент.
Исходная концепция дизайна интерфейса сокета, различающая типы (семейства) протоколов и конкретные типы адресов, которые каждый может использовать. Предполагалось, что семейство протоколов может иметь несколько типов адресов. Типы адресов определялись дополнительными символическими константами с использованием префикса AF вместо PF. AF-идентификаторы предназначены для всех структур данных, которые конкретно имеют дело с типом адреса, а не семейством протоколов. Однако эта концепция разделения протокола и типа адреса не нашла поддержки в реализации, и константы AF были определены соответствующим идентификатором протокола, оставляя различие между константами AF и PF в качестве технического аргумента. не имеет практических последствий. Действительно, существует большая путаница в правильном использовании обеих форм.
Спецификация POSIX.1-2008 не определяет никаких PF-констант, а только AF-констант
Необработанные сокеты предоставляют простой интерфейс, который обходит обработку стеком TCP / IP хоста. Они позволяют реализовать сетевые протоколы в пользовательском пространстве и помогают в отладке стека протоколов. Необработанные сокеты используются некоторыми службами, такими как ICMP, которые работают на Интернет-уровне модели TCP / IP.
После создания сокета можно установить для него параметры. Некоторые из наиболее распространенных опций:
Сокеты Berkeley могут работать в одном из двух режимов: блокирующем или неблокирующем.
Блокирующий сокет не возвращает управление до тех пор, пока он не отправит (или не получит) некоторые или все данные, указанные для операции. Блокирующий сокет не отправляет все данные - это нормально. Приложение должно проверить возвращаемое значение, чтобы определить, сколько байтов было отправлено или получено, и оно должно повторно отправить любые данные, которые еще не обработаны. При использовании блокирующих сокетов особое внимание следует уделить accept (), поскольку он все еще может блокироваться после индикации удобочитаемости, если клиент отключается во время фазы подключения.
С другой стороны, неблокирующий сокет возвращает все, что находится в приемном буфере, и немедленно продолжает работу. В случае неправильного написания программы, использующие неблокирующие сокеты, особенно подвержены условиям гонки из-за разницы в скорости сетевого соединения.
Сокет обычно устанавливается в режим блокировки или неблокирования с помощью функций fcntl () или ioctl ().
Операционная система не освобождает ресурсы, выделенные для сокета, пока сокет не будет закрыт. Это особенно важно, если вызов подключения завершится неудачно и будет повторяться.
Когда приложение закрывает сокет, уничтожается только интерфейс к сокету. Ядро несет ответственность за внутреннее уничтожение сокета. Иногда сокет может переходить в состояние TIME_WAIT на стороне сервера на срок до 4 минут.
В системах SVR4 используется close ()
может отбросить данные. В этих системах может потребоваться использование shutdown ()
или SO_LINGER, чтобы гарантировать доставку всех данных.
Передача Протокол управления (TCP) - это ориентированный на соединение протокол, который обеспечивает множество функций исправления ошибок и повышения производительности для передачи байтовых потоков. Процесс создает сокет TCP, вызывая функцию socket ()
с параметрами для семейства протоколов (PF INET, PF_INET6), режим сокета для Stream Сокеты (SOCK_STREAM) и идентификатор протокола IP для TCP (IPPROTO_TCP).
Установка TCP-сервера включает следующие основные шаги:
Следующая программа создает TCP-сервер, прослушивающий порт number 1100:
#include#include #include #include #include #include #include #include int main (void) {struct sockaddr_in sa; int SocketFD = сокет (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (SocketFD == -1) {ошибка ("не удается создать сокет"); выход (EXIT_FAILURE); } memset (sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_port = htons (1100); sa.sin_addr.s_addr = htonl (INADDR_ANY); если (привязка (SocketFD, (struct sockaddr *) sa, sizeof sa) == -1) {perror («сбой привязки»); закрыть (SocketFD); выход (EXIT_FAILURE); } если (прослушивание (SocketFD, 10) == -1) {ошибка ("прослушивание не удалось"); закрыть (SocketFD); выход (EXIT_FAILURE); } for (;;) {int ConnectFD = accept (SocketFD, NULL, NULL); if (0>ConnectFD) {ошибка ("ошибка принятия"); закрыть (SocketFD); выход (EXIT_FAILURE); } / * выполняем операции чтения и записи... чтение (ConnectFD, buff, size) * / if (shutdown (ConnectFD, SHUT_RDWR) == -1) {perror ("завершение не выполнено"); закрыть (ConnectFD); закрыть (SocketFD); выход (EXIT_FAILURE); } закрыть (ConnectFD); } закрыть (SocketFD); вернуть EXIT_SUCCESS; }
Программирование клиентского приложения TCP включает следующие шаги:
sockaddr_in
структура с sin_family
, установленным на AF_INET, sin_port
, установленным на порт, который конечная точка прослушивает (в сетевом порядке байтов), и sin_addr
установлен на IP-адрес слушающего сервера (также в сетевом порядке байтов.)#include#include #include #include #include #include #include #include int main (void) {struct sockaddr_in sa; int res; int SocketFD; SocketFD = сокет (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (SocketFD == -1) {ошибка ("не удается создать сокет"); выход (EXIT_FAILURE); } memset (sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_port = htons (1100); res = inet_pton (AF_INET, «192.168.1.3», sa.sin_addr); if (connect (SocketFD, (struct sockaddr *) sa, sizeof sa) == -1) {perror ("сбой подключения"); закрыть (SocketFD); выход (EXIT_FAILURE); } / * выполняем операции чтения-записи... * / close (SocketFD); вернуть EXIT_SUCCESS; }
Протокол дейтаграмм пользователя (UDP) - это протокол без установления соединения без гарантии доставки. Пакеты UDP могут приходить не по порядку, несколько раз или не приходить вовсе. Благодаря такой минималистичной конструкции, UDP значительно меньше накладных расходов, чем TCP. Отсутствие соединения означает, что нет концепции потока или постоянного соединения между двумя хостами. Такие данные называются дейтаграммами (Datagram Sockets ).
Адресное пространство UDP, пространство номеров портов UDP (в терминологии ISO, TSAP ), полностью не пересекается с пространством портов TCP.
Приложение может настроить UDP-сервер на порт номер 7654 следующим образом. Программа содержит бесконечный цикл, который принимает дейтаграммы UDP с функцией recvfrom ().
#include#include #include #include #include #include #include / * for close () для сокета * / #include int main (void) {int sock; struct sockaddr_in sa; char buffer [1024]; ssize_t recsize; socklen_t fromlen; memset (sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl (INADDR_ANY); sa.sin_port = htons (7654); fromlen = sizeof sa; sock = сокет (PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (bind (sock, (struct sockaddr *) sa, sizeof sa) == -1) {perror («ошибка привязки не удалась»); близко (носок); выход (EXIT_FAILURE); } for (;;) {recsize = recvfrom (sock, (void *) buffer, sizeof buffer, 0, (struct sockaddr *) sa, fromlen); if (recsize < 0) { fprintf(stderr, "%s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("recsize: %d\n ", (int)recsize); sleep(1); printf("datagram: %.*s\n", (int)recsize, buffer); } }
Ниже представлена клиентская программа для отправки UDP-пакета, содержащего строку «Hello World!», на адрес 127.0.0.1 с номером порта 7654.
#include#include #include #include #include #include #include #include #include int main (void) {int sock; struct sockaddr_in sa; int bytes_sent; char buffer [200]; strcpy (buffer, "hello world!"); / * создать Интернет, дейтаграмму, сокет с использованием UDP * / sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock == -1) {/ * если не удалось инициализировать сокет, выйти * / printf ("Ошибка создания сокета"); exit (EXIT_FAILURE);} / * Обнулить адрес сокета * / memset (sa, 0, sizeof sa); / * Адрес IPv4 * / sa.sin_family = AF_INET; / * IPv4-адреса - это uint32_t, преобразовываем строковое представление октетов в соответствующее значение * / sa.sin_addr.s_addr = inet_addr ("127.0.0.1"); / * сокеты - это беззнаковые шорты, htons (x) гарантирует, что x находится в сетевом порядке байтов, установите порт на 7654 * / sa.sin_por t = htons (7654); bytes_sent = sendto (сок, буфер, strlen (буфер), 0, (struct sockaddr *) sa, sizeof sa); if (bytes_sent < 0) { printf("Error sending packet: %s\n", strerror(errno)); exit(EXIT_FAILURE); } close(sock); /* close the socket */ return 0; }
В этом коде буфер - это указатель на данные, которые должны быть отправлены, а длина_буфера определяет размер данных.
de jure стандартное определение интерфейса Sockets содержится в стандарте POSIX, известном как:
Информация об этом стандарте и текущей работе над ним доступна на веб-сайте Austin.
Расширения IPv6 для API базового сокета описаны в RFC 3493 и RFC 3542.
Эта статья основана на материалах, взятых из Free On-line Dictionary of Computing до 1 ноября 2008 г. и включенных в раздел " перелицензирование »условий GFDL версии 1.3 или новее.