stringtranslate.com

Розетки Беркли

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

Сокет — это абстрактное представление ( дескриптор ) локальной конечной точки сетевого пути связи. API сокетов Беркли представляет его как файловый дескриптор ( дескриптор файла ) в философии Unix , который обеспечивает общий интерфейс для ввода и вывода потоков данных.

Сокеты Беркли с небольшими изменениями превратились из фактического стандарта в компонент спецификации POSIX . Термин сокеты POSIX по сути является синонимом сокетов Беркли , но они также известны как сокеты BSD , что означает первую реализацию в дистрибутиве программного обеспечения Беркли .

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

Сокеты Беркли возникли в операционной системе 4.2BSD Unix , выпущенной в 1983 году, в качестве программного интерфейса. Однако только в 1989 году Калифорнийский университет в Беркли смог выпустить версии операционной системы и сетевой библиотеки, свободные от лицензионных ограничений, присущих Unix, принадлежащей корпорации AT&T .

Все современные операционные системы реализуют версию интерфейса сокетов Беркли. Он стал стандартным интерфейсом для приложений, работающих в Интернете . Даже реализация Winsock для MS Windows, созданная независимыми разработчиками, точно соответствует стандарту.

API сокетов BSD написан на языке программирования C. Большинство других языков программирования предоставляют аналогичные интерфейсы, обычно написанные в виде библиотеки-оболочки на основе C API. [1]

Сокеты BSD и POSIX.

По мере того, как API сокетов Беркли развивался и в конечном итоге превратился в API сокетов POSIX, [2] некоторые функции устарели или были удалены и заменены другими. POSIX API также предназначен для повторного входа и поддерживает IPv6.

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

API интерфейса транспортного уровня (TLI) на основе STREAMS предлагает альтернативу API сокетов. Многие системы, предоставляющие API TLI, также предоставляют API сокетов Беркли.

Не-Unix-системы часто предоставляют API сокетов Беркли с уровнем трансляции в собственный сетевой API. Plan 9 [3] и Genode [4] используют API файловой системы с управляющими файлами, а не с файловыми дескрипторами.

Заголовочные файлы

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

Функции API сокетов

Блок-схема транзакции клиент-сервер с использованием сокетов с протоколом управления передачей (TCP).

API сокетов Беркли обычно предоставляет следующие функции:

разъем

Функция socket() создает конечную точку для связи и возвращает файловый дескриптор сокета. Он использует три аргумента:

Функция возвращает -1, если произошла ошибка. В противном случае он возвращает целое число, представляющее вновь назначенный дескриптор.

связывать

bind() связывает сокет с адресом. Когда сокет создается с помощью функции socket() , ему присваивается только семейство протоколов, но не адрес. Эта ассоциация должна быть выполнена до того, как сокет сможет принимать соединения от других хостов. Функция имеет три аргумента:

Функция Bind() возвращает 0 в случае успеха и -1 в случае возникновения ошибки.

слушать

После того, как сокет связан с адресом, метод Listen() подготавливает его для входящих соединений. Однако это необходимо только для потоково-ориентированных (ориентированных на соединение) режимов данных, т.е. для типов сокетов ( SOCK_STREAM , SOCK_SEQPACKET ). Listen() требует два аргумента:

Как только соединение принято, оно выводится из очереди. В случае успеха возвращается 0. В случае возникновения ошибки возвращается -1.

принимать

Когда приложение прослушивает потоковые соединения от других хостов, оно уведомляется о таких событиях (см. функцию select() ) и должно инициализировать соединение с помощью функции Accept() . Он создает новый сокет для каждого соединения и удаляет соединение из очереди прослушивания. Функция имеет следующие аргументы:

Accept() возвращает новый дескриптор сокета для принятого соединения или значение -1 в случае возникновения ошибки. Вся дальнейшая связь с удаленным хостом теперь происходит через этот новый сокет.

Сокеты дейтаграмм не требуют обработки с помощью метода Accept(), поскольку получатель может немедленно ответить на запрос, используя прослушивающий сокет.

соединять

Connect() устанавливает прямую связь с конкретным удаленным хостом, идентифицируемым его адресом, через сокет, идентифицируемый его файловым дескриптором.

При использовании протокола , ориентированного на соединение , устанавливается соединение. Некоторые типы протоколов не поддерживают соединение, в первую очередь протокол пользовательских дейтаграмм . При использовании с протоколами без установления соединения Connect определяет удаленный адрес для отправки и получения данных, позволяя использовать такие функции, как send и Recv . В этих случаях функция подключения предотвращает прием датаграмм из других источников.

Connect() возвращает целое число, представляющее код ошибки: 0 представляет успех, а –1 представляет ошибку. Исторически сложилось так, что в системах, основанных на BSD, состояние дескриптора сокета не определено, если вызов подключения завершается неудачно (как указано в Единой спецификации Unix), поэтому переносимые приложения должны немедленно закрыть дескриптор сокета и получить новый дескриптор с помощью Socket(), в случае сбоя вызова метода Connect(). [5]

gethostbyname и gethostbyaddr

Функции gethostbyname() и gethostbyaddr() используются для разрешения имен и адресов хостов в системе доменных имен или других механизмах разрешения локальных хостов (например, поиск в /etc/hosts). Они возвращают указатель на объект типа struct hostent , который описывает хост интернет-протокола . Функции используют следующие аргументы:

Функции возвращают NULL-указатель в случае ошибки, и в этом случае можно проверить внешнее целое число h_errno , чтобы определить, является ли это временным сбоем или недействительным или неизвестным хостом. В противном случае возвращается действительная структура hostent * .

Эти функции не являются строго компонентами API сокетов BSD, но часто используются вместе с функциями API для поиска хоста. Эти функции теперь считаются устаревшими интерфейсами для запроса системы доменных имен. Определены новые функции, полностью независимые от протокола (поддержка IPv6). Эти новые функции — getaddrinfo() и getnameinfo() — основаны на новой структуре данных addrinfo . [6]

Эта пара функций появилась одновременно с API сокетов BSD в версии 4.2BSD (1983), [7] в том же году, когда впервые был создан DNS. Ранние версии не запрашивали DNS и выполняли только поиск в /etc/hosts. В версии 4.3BSD (1984 г.) DNS был добавлен в грубом виде. Текущая реализация, использующая переключатель службы имен, является производной от Solaris и более поздней версии NetBSD 1.4 (1999). [8] Изначально созданный для NIS+ , NSS делает DNS лишь одним из многих вариантов поиска с помощью этих функций, и его использование можно отключить даже сегодня. [9]

Протокольные и адресные семьи

API сокетов Беркли — это общий интерфейс для сетевого и межпроцессного взаимодействия, поддерживающий использование различных сетевых протоколов и адресных архитектур.

Ниже приводится выборка семейств протоколов (перед которыми указан стандартный символический идентификатор), определенных в современной реализации Linux или BSD :

Сокет для связи создается с помощью socket()функции путем указания желаемого семейства протоколов ( PF_ -идентификатор) в качестве аргумента.

Первоначальная концепция дизайна интерфейса сокетов различала типы протоколов (семейства) и конкретные типы адресов, которые каждый из них может использовать. Предполагалось, что семейство протоколов может иметь несколько типов адресов. Типы адресов определялись дополнительными символьными константами с использованием префикса AF вместо PF . Идентификаторы AF предназначены для всех структур данных, которые конкретно связаны с типом адреса, а не с семейством протоколов. Однако эта концепция разделения типа протокола и адреса не нашла поддержки в реализации, и константы AF определялись соответствующим идентификатором протокола, в результате чего различие между константами AF и PF оставалось техническим аргументом, не имеющим практического значения. Действительно, существует большая путаница в правильном использовании обеих форм. [11]

Спецификация POSIX.1—2008 не определяет никаких PF -констант, а только AF -константы [12]

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

Необработанные сокеты предоставляют простой интерфейс, который обходит обработку стека TCP/IP хоста. Они позволяют реализовать сетевые протоколы в пользовательском пространстве и помогают в отладке стека протоколов. [13] Необработанные сокеты используются некоторыми службами, такими как ICMP , которые работают на уровне Интернета модели TCP/IP.

Блокирующий и неблокирующий режим

Сокеты Беркли могут работать в одном из двух режимов: блокирующем или неблокирующем.

Блокирующий сокет не возвращает управление до тех пор, пока не отправит (или не получит) некоторые или все данные, указанные для операции. Блокирующий сокет не отправляет все данные — это нормально. Приложение должно проверить возвращаемое значение, чтобы определить, сколько байт было отправлено или получено, и повторно отправить все еще не обработанные данные. [14] При использовании блокирующих сокетов особое внимание следует уделить функции Accept(), поскольку она все равно может блокироваться после указания читаемости, если клиент отключается на этапе подключения.

Неблокирующий сокет возвращает все, что находится в буфере приема, и немедленно продолжает работу. Если они написаны неправильно, программы, использующие неблокирующие сокеты, особенно подвержены состояниям гонки из-за различий в скорости сетевого соединения. [ нужна цитата ]

Сокет обычно устанавливается в блокирующий или неблокирующий режим с помощью функций fcntl и ioctl .

Оконечные розетки

Операционная система не освобождает ресурсы, выделенные для сокета, пока сокет не будет закрыт. Это особенно важно, если вызов подключения завершается неудачно и будет повторен.

Когда приложение закрывает сокет, уничтожается только интерфейс сокета. Ядро несет ответственность за внутреннее уничтожение сокета. Иногда сокет может войти в состояние TIME_WAIT на стороне сервера на срок до 4 минут. [15]

В системах SVR4 использование close()может привести к потере данных. В этих системах может потребоваться использование shutdown()SO_LINGER, чтобы гарантировать доставку всех данных. [16]

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

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

Сервер

Установка TCP-сервера включает в себя следующие основные шаги:

Следующая программа создает TCP-сервер, прослушивающий порт номер 1100:

 #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; int SocketFD = сокет ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( SocketFD == -1 ) { perror ( «невозможно создать сокет» ); выход ( EXIT_FAILURE ); } Memset ( & sa , 0 , sizeof sa ); са . sin_family = AF_INET ; са . sin_port = htons ( 1100 ); са . грех_адрес . s_addr = htonl ( INADDR_ANY ); if ( bind ( SocketFD ,( struct sockaddr * ) & sa , sizeof sa ) == -1 ) { perror ( "связка не удалась" ); закрыть ( SocketFD ); выход ( EXIT_FAILURE ); } if ( listen ( SocketFD , 10 ) == -1 ) { perror ( «прослушивание не удалось» ); закрыть ( SocketFD ); выход ( EXIT_FAILURE ); } for (;;) { int ConnectFD = Accept ( SocketFD , NULL , NULL ); if ( ConnectFD == -1 ) { perror ( «не удалось принять» ); закрыть ( SocketFD );                                                                                               выход ( EXIT_FAILURE ); } /* выполняем операции чтения и записи...  read(ConnectFD, buff, size)  */ if ( shutdown ( ConnectFD , SHUT_RDWR ) == -1 ) { perror ( "shutdown error" ); закрыть ( ConnectFD ); закрыть ( SocketFD ); выход ( EXIT_FAILURE ); } закрыть ( ConnectFD ); }                  закрыть ( SocketFD ); вернуть EXIT_SUCCESS ; }   

Клиент

Программирование клиентского приложения TCP включает в себя следующие шаги:

 #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main ( void ) { struct sockaddr_in sa ; целое разрешение ; интервал SocketFD ;                           SocketFD = сокет ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( SocketFD == -1 ) { perror ( «невозможно создать сокет» ); выход ( EXIT_FAILURE ); } Memset ( & sa , 0 , sizeof sa ); са . sin_family = AF_INET ; са . 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 могут приходить не по порядку, несколько раз или не приходить вообще. Из-за такой минимальной конструкции UDP имеет значительно меньшие издержки, чем TCP. Отсутствие соединения означает, что между двумя хостами не существует понятия потока или постоянного соединения. Такие данные называются датаграммами ( датаграммными сокетами ).

Адресное пространство UDP, пространство номеров портов UDP (в терминологии ISO, TSAP ) , полностью не пересекается с пространством портов TCP.

Сервер

Приложение может настроить UDP-сервер на порту 7654 следующим образом. Программы содержат бесконечный цикл, который получает датаграммы UDP с помощью функции Recvfrom() .

#include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> /* для close() для сокета */ #include <stdlib.h>        int main ( void ) { int sock ; структура sockaddr_in sa ; буфер символов [ 1024 ]; ssize_t изменить размер ; socklen_t отлен ;              memset ( & sa , 0 , sizeof sa ); са . sin_family = AF_INET ; са . грех_адрес . s_addr = htonl ( INADDR_ANY ); са . sin_port = htons ( 7654 ); fromlen = sizeof sa ;                 носок = сокет ( 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_FAILURE ); } printf ( "recsize: %d \n " , ( int ) Recsize ); спать ( 1 ); printf ( "дейтаграмма: %.*s \n " , ( int ) Recsize , буфер ); } }                             

Клиент

Ниже представлена ​​клиентская программа для отправки UDP-пакета, содержащего строку «Hello World!» по адресу 127.0.0.1 по номеру порта 7654.

#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet /in.h> #include <unistd.h> #include <arpa/inet.h>         int main ( void ) { int sock ; структура sockaddr_in sa ; int байт_отправлено ; буфер символов [ 200 ]; strcpy ( buffer , «Привет, мир!» ); /* создаем Интернет, датаграмму, сокет с использованием UDP */ sock = сокет ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( sock == -1 ) { /* если сокет не удалось инициализировать, выходим */ printf ( «Ошибка создания сокета» ); выход ( EXIT_FAILURE ); } /* Обнуляемый адрес сокета */ memset ( & sa , 0 , sizeof sa ); /* Адрес IPv4 */ sa . sin_family = AF_INET ; /* IPv4-адреса представляют собой uint32_t, преобразуют строковое представление октетов в соответствующее значение */ sa . грех_адрес . s_addr = inet_addr ( "127.0.0.1" ); /* сокеты представляют собой беззнаковые шорты, htons(x) гарантирует, что x находится в сетевом порядке байтов, установите порт на 7654 */ sa . sin_port = htons ( 7654 ); bytes_sent = sendto ( sock , buffer , strlen ( buffer ), 0 , ( struct sockaddr * ) & sa , sizeof sa ); if ( bytes_sent < 0 ) { printf ( «Ошибка отправки пакета: %s \n » , strerror ( errno )); выход ( EXIT_FAILURE ); } закрыть ( носок ); /* закрываем сокет */ return 0 ; }                                                                          

В этом коде buffer — это указатель на данные, которые необходимо отправить, а buffer_length указывает размер данных.

Рекомендации

  1. ^ Например, на языке программирования Ruby Ruby-doc::Socket
  2. ^ «— Спецификация POSIX.1-2008». Opengroup.org . Проверено 26 июля 2012 г.
  3. ^ «Организация сетей в Плане 9».
  4. ^ «Стек TCP/IP Linux как плагин VFS» .
  5. ^ Стивенс и Раго 2013, с. 607.
  6. ^ POSIX.1-2004
  7. ^ gethostbyname(3)  -  Руководство по функциям библиотеки FreeBSD.
  8. Конилл, Ариадна (27 марта 2022 г.). «Трагедия gethostbyname». ariadne.space .
  9. ^ nsswitch.conf(5)  -  Руководство по форматам файлов FreeBSD.
  10. ^ https://manpages.debian.org/experimental/ax25-tools/netrom.4.en.html. {{cite web}}: Отсутствует или пусто |title=( помощь )
  11. ^ Сетевое программирование UNIX, том 1, третье издание: API сетевых сокетов, В. Ричард Стивенс, Билл Феннер, Эндрю М. Рудофф, Эддисон Уэсли, 2003.
  12. ^ «Базовые спецификации открытой группы, выпуск 7» . Pubs.opengroup.org . Проверено 26 июля 2012 г.
  13. ^ «Необработанные сокеты TCP/IP — приложения Win32» . 19 января 2022 г.
  14. ^ «Руководство Биджа по сетевому программированию». Beej.us. 05.05.2007 . Проверено 26 июля 2012 г.
  15. ^ «завершающие сокеты». Softlab.ntua.gr . Проверено 26 июля 2012 г.
  16. ^ «ntua.gr — Программирование сокетов UNIX на C — Часто задаваемые вопросы: вопросы, касающиеся как клиентов, так и серверов (TCP/SOCK_STREAM)» . Softlab.ntua.gr . Проверено 26 июля 2012 г.

Стандартное определение интерфейса Sockets де-юре содержится в стандарте POSIX, известном как:

Информацию об этом стандарте и текущей работе над ним можно найти на веб-сайте Остина.

Расширения IPv6 для API базового сокета документированы в RFC 3493 и RFC 3542.

Внешние ссылки