Skip to content

Сокеты

Общие сведения о сокетах

Понятие сокета

Сокеты обеспечивают двухстороннюю связь типа ``точка-точка'' между двумя процессами. Они являются основными компонентами межсистемной и межпроцессной связи. Каждый сокет представляет собой конечную точку связи, с которой может быть совмещено некоторое имя. Он имеет определенный тип, и один процесс или несколько, связанных с ним процессов.

Сокеты находятся в областях связи (доменах). Домен сокета - это абстракция, которая определяет структуру адресации и набор протоколов. Сокеты могут соединяться только с сокетами в том же домене. Всего выделено 23 класса сокетов (см. файл ), из которых обычно используются только UNIX-сокеты и Интернет-сокеты. Сокеты могут использоваться для установки связи между процессами на отдельной системе подобно другим формам IPC.

Класс сокетов UNIX обеспечивает их адресное пространство для отдельной вычислительной системы. Сокеты области UNIX называются именами файлов UNIX. Сокеты также можно использовать, чтобы организовать связь между процессами на различных системах. Адресное пространство сокетов между связанными системами называют доменом Интернета. Коммуникации домена Интернета используют стек протоколов TCP/IP.

Сокет (socket) - это конечная точка сетевых коммуникаций. Он является чем-то вроде "портала", через которое можно отправлять байты во внешний мир. Приложение просто пишет данные в сокет; их дальнейшая буферизация, отправка и транспортировка осуществляется используемым стеком протоколов и сетевой аппаратурой. Чтение данных из сокета происходит аналогичным образом.

В программе сокет идентифицируется дескриптором - это просто переменная типа int. Программа получает дескриптор от операционной системы при создании сокета, а затем передаёт его сервисам socket API для указания сокета, над которым необходимо выполнить то или иное действие.

Атрибуты сокета

С каждым сокет связываются три атрибута: домен, тип и протокол. Эти атрибуты задаются при создании сокета и остаются неизменными на протяжении всего времени его существования.

Домен определяет пространство адресов, в котором располагается сокет, и множество протоколов, которые используются для передачи данных. Чаще других используются домены Unix и Internet, задаваемые константами AF_UNIX и AF_INET соответственно (префикс AF означает "address family" - "семейство адресов"). При задании AF_UNIX для передачи данных используется файловая система ввода/вывода Unix. В этом случае сокеты используются для межпроцессного взаимодействия на одном компьютере и не годятся для работы по сети. Константа AF_INET соответствует Internet-домену. Сокеты, размещённые в этом домене, могут использоваться для работы в любой IP-сети.

Тип сокета определяет способ передачи данных по сети. Чаще других применяются:

  • SOCK_STREAM. Передача потока данных с предварительной установкой соединения. Обеспечивается надёжный канал передачи данных, при котором фрагменты отправленного блока не теряются, не переупорядочиваются и не дублируются.
  • SOCK_DGRAM. Передача данных в виде отдельных сообщений (датаграмм). Предварительная установка соединения не требуется. Обмен данными происходит быстрее, но является ненадёжным: сообщения могут теряться в пути, дублироваться и переупорядочиваться. Допускается передача сообщения нескольким получателям (multicasting) и широковещательная передача (broadcasting).
  • SOCK_RAW. Этот тип присваивается низкоуровневым (т. н. "сырым") сокетам. Их отличие от обычных сокетов состоит в том, что с их помощью программа может взять на себя формирование некоторых заголовков, добавляемых к сообщению.

Обратите внимание, что не все домены допускают задание произвольного типа сокета. Например, совместно с доменом Unix используется только тип SOCK_STREAM. С другой стороны, для Internet-домена можно задавать любой из перечисленных типов. В этом случае для реализации SOCK_STREAM используется протокол TCP, для реализации SOCK_DGRAM - протокол UDP, а тип SOCK_RAW используется для низкоуровневой работы с протоколами IP, ICMP и т. д.

Адреса

Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене (эту процедуру называют именованием сокета). Иногда связывание осуществляется неявно (внутри функций connect и accept), но выполнять его необходимо во всех случаях. Вид адреса зависит от выбранного вами домена. В Unix-домене это текстовая строка - имя файла, через который происходит обмен данными. В Internet-домене адрес задаётся комбинацией IP-адреса и 16-битного номера порта. IP-адрес определяет хост в сети, а порт - конкретный сокет на этом хосте. Протоколы TCP и UDP используют различные пространства портов.

Установка соединения (сервер)

Установка соединения на стороне сервера состоит из четырёх этапов, ни один из которых не может быть опущен.

Сначала сокет создаётся и привязывается к локальному адресу. Если компьютер имеет несколько сетевых интерфейсов с различными IP-адресами, вы можете принимать соединения только с одного из них, передав его адрес функции bind. Если же вы готовы соединяться с клиентами через любой интерфейс, задайте в качестве адреса константу INADDR_ANY. Что касается номера порта, вы можете задать конкретный номер или 0 (в этом случае система сама выберет произвольный неиспользуемый в данный момент номер порта).

На следующем шаге создаётся очередь запросов на соединение. При этом сокет переводится в режим ожидания запросов со стороны клиентов. Всё это выполняет функция listen.

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

Функция accept создаёт для общения с клиентом новый сокет и возвращает его дескриптор. Параметр sockfd задаёт слушающий сокет. После вызова он остаётся в слушающем состоянии и может принимать другие соединения. В структуру, на которую ссылается addr, записывается адрес сокета клиента, который установил соединение с сервером.

Обратите внимание, что полученный от accept новый сокет связан с тем же самым адресом, что и слушающий сокет. Сначала это может показаться странным. Но дело в том, что адрес TCP-сокета не обязан быть уникальным в Internet-домене. Уникальными должны быть только соединения, для идентификации которых используются два адреса сокетов, между которыми происходит обмен данными.

Установка соединения (клиент)

На стороне клиента для установления соединения используется функция connect. Обычно сокет не требуется предварительно привязывать к локальному адресу, так как функция connect сделает это за вас, подобрав подходящий свободный порт. Вы можете принудительно назначить клиентскому сокету некоторый номер порта, используя bind перед вызовом connect. Делать это следует в случае, когда сервер соединяется с только с клиентами, использующими определённый порт (примерами таких серверов являются rlogind и rshd). В остальных случаях проще и надёжнее предоставить системе выбрать порт за вас.

Обмен данными

После того как соединение установлено, можно начинать обмен данными. Для этого используются функции send и recv. В Unix для работы с сокетами можно использовать также файловые функции read и write, но они обладают меньшими возможностями.

Закрытие сокета

Закончив обмен данными, закройте сокет с помощью функции close. Это приведёт к разрыву соединения.

Вы также можете запретить передачу данных в каком-то одном направлении, используя shutdown.


Сетевые сокеты

Рассмотрим, как процессы выполняют работу считывания данных и записи данных в сеть. Для процессов достаточно просто считывать и записывать данные в уже настроенные сетевые соединения: все, что вам нужно, — это несколько системных вызовов, о которых вы можете прочитать на страницах руководства recv(2) и send(2). С точки зрения процесса самое важное, что нужно знать, — как получить доступ к сети при использовании этих системных вызовов. В системах Unix процесс задействует сокет для определения того, когда и как он взаимодействует с сетью.

Сокеты (sockets) — это интерфейс, с помощью которого процессы получают доступ к сети через ядро, они представляют собой границу между пространством пользователя и пространством ядра. Их часто применяют для межпроцессного взаимодействия (Inter Process Communication, IPC).

Существуют разные типы сокетов, потому что процессы должны получать доступ к сети разными способами. Например, TCP-соединения представлены потоковыми сокетами (SOCK_STREAM с точки зрения программиста), а UDP-соединения — сокетами датаграмм (SOCK_DGRAM).

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

Прием и обработка входящих подключений

Данная блок-схема показывает, сколько серверов обрабатывают подключения для сокетов входящего потока. Обратите внимание на то, что этот тип сервера включает в себя два вида сокетов: один для прослушивания и один для чтения и записи. Главный процесс задействует прослушивающий сокет для поиска подключений из сети. Когда появляется новое соединение, главный процесс использует системный вызов accept(), чтобы принять соединение, которое создает для себя сокет для чтения/записи. Затем главный процесс применяет вызов fork() для создания нового дочернего процесса, обрабатывающего соединение. Наконец, исходный сокет как прослушиватель продолжает искать дополнительные соединения от имени главного процесса.

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


Доменные сокеты Unix

Приложения, использующие сетевые средства, не обязательно должны включать два отдельных хоста. Многие из них построены как клиент-серверные или механизмы «узел — узел», где процессы, запущенные на одной машине, используют межпроцессную связь для согласования того, какую работу необходимо выполнить и кто ее станет делать. Например, вспомните, что демоны, такие как systemd и NetworkManager, применяли D-Bus для мониторинга и реагирования на системные события.

Процессы способны использовать обычную IP-сеть через локальный хост (127.0.0.1 или ::1) для связи друг с другом, но обычно они задействуют в качестве альтернативы специальный тип сокета, называемый доменным сокетом Unix. Когда процесс подключается к доменному сокету Unix, он ведет себя почти так же, как с сетевым сокетом: прослушивает и принимает соединения в сокете и позволяет выбирать между различными типами сокетов, чтобы действовать как TCP или UDP.

Имейте в виду, что доменный сокет Unix не является сетевым сокетом и за ним нет никакой сети. Вам даже не нужно настраивать сеть для его использования. Доменные сокеты Unix также не обязательно должны быть привязаны к файлам сокетов. Процесс может создать безымянный доменный сокет Unix и поделиться адресом с другим процессом.

Доменные сокеты Unix для IPC нравятся разработчикам по двум причинам.

  • Во-первых, они позволяют использовать специальные файлы сокетов в файловой системе для управления доступом, поэтому любой процесс, у которого нет доступа к файлу сокета, не может его применять. И поскольку нет взаимодействия с сетью, она проще и менее подвержена обычным сетевым вторжениям. Например, файл сокета для D-Bus можно найти в /var/run/dbus

  • Во-вторых, поскольку ядру Linux не нужно проходить через множество уровней своей сетевой подсистемы во время работы с доменными сокетами Unix, производительность, как правило, намного выше.

Создание кода для доменных сокетов Unix не сильно отличается от поддержки обычных сетевых сокетов. Поскольку преимущества могут быть значительными, некоторые сетевые серверы предлагают связь как через сетевые, так и через доменные сокеты Unix. Например, mysqld — сервер базы данных MySQL — может принимать клиентские подключения от удаленных хостов, но обычно он также предлагает доменный сокет Unix в /var/run/mysqld/mysqld.sock.

Вы можете просмотреть список доменных сокетов Unix, которые в данный момент используются в вашей системе, с помощью команды lsof -U


Полезные ссылки

Программирование сокетов в Linux

Как Linux создаёт и подсчитывает сокеты