Что такое сокет Linux
Обновлено: 21.11.2024
Сокет — это механизм, обеспечивающий соединение двухпроцессорной системы с помощью сетевых стеков. Более понятным способом мы можем использовать сокеты для связи и передачи данных между двумя системами. Как мы знаем, системы Unix и Linux работают на файловой логике. Все в операционной системе является файлом, и сетевые подключения также являются файлами.
Случаи использования
Сокеты можно использовать в разных случаях.
- Мы можем использовать сокет для передачи данных между двумя разными процессами в одной системе.
- Отправить команду в удаленную систему
- Загрузить данные из удаленной системы
Типы сокетов
Существует четыре основных типа сокетов. Потоковые сокеты и сокеты дейтаграмм являются популярными типами.
Сокет потока
Потоковые сокеты используют для передачи протокол TCP. Таким образом, это делает потоковый сокет надежным способом передачи. Если есть проблема, связанная с сетью, мы получим сообщения об ошибках. Если мы отправим данные X, Y, Z, удаленное устройство получит их в том же порядке X, Y, Z.
Потоковое сокет
Сокет датаграммы
Дейтаграммные сокеты используют UDP, что делает доставку данных несогласованной. Еще одна особенность сокета дейтаграммы — отсутствие соединения. Отправитель просто отправляет данные и не может быть уверен, что они доставлены адресату.
Необработанный сокет
Как следует из названия, необработанные сокеты не предоставляют готовый к использованию протокол. Он в основном используется для создания нового протокола, понятного обеим сторонам. Необработанные сокеты не используются регулярно.
Последовательный пакетный сокет
Sequenced Packet Socket похож на Stream Socket, но имеет границы записи. Sequnced Packet Socket не является популярным способом использования сокета.
Java-сокет
Сокет Python
Язык программирования Python также предоставляет библиотеки, связанные с Socket. Мы можем импортировать библиотеку Socket с помощью следующего кода. Python также поддерживает шифрование связи через сокеты.
Сокеты обеспечивают связь между двумя разными процессами на одном или разных компьютерах. Точнее, это способ общения с другими компьютерами с использованием стандартных файловых дескрипторов Unix. В Unix каждое действие ввода-вывода выполняется путем записи или чтения файлового дескриптора. Дескриптор файла — это просто целое число, связанное с открытым файлом, и это может быть сетевое соединение, текстовый файл, терминал или что-то еще.
Для программиста сокет выглядит и ведет себя так же, как низкоуровневый файловый дескриптор. Это связано с тем, что такие команды, как read() и write(), работают с сокетами так же, как с файлами и каналами.
Сокеты были впервые представлены в 2.1BSD, а затем доработаны до их текущей формы в 4.2BSD. Функция сокетов теперь доступна в большинстве последних выпусков систем UNIX.
Где используется сокет?
Сокет Unix используется в среде клиент-серверных приложений. Сервер — это процесс, выполняющий некоторые функции по запросу клиента. Большинство протоколов прикладного уровня, таких как FTP, SMTP и POP3, используют сокеты для установления соединения между клиентом и сервером, а затем для обмена данными.
Типы сокетов
Пользователям доступно четыре типа сокетов. Первые два используются чаще всего, а последние два редко.
Предполагается, что процессы обмениваются данными только между сокетами одного типа, но нет ограничений, препятствующих обмену данными между сокетами разных типов.
Потоковые сокеты — доставка в сетевом окружении гарантирована. Если вы отправите через потоковый сокет три элемента «A, B, C», они прибудут в том же порядке — «A, B, C». Эти сокеты используют TCP (протокол управления передачей) для передачи данных. Если доставка невозможна, отправитель получает индикатор ошибки. Записи данных не имеют границ.
Сокеты дейтаграмм. Доставка в сетевом окружении не гарантируется. Они не требуют установления соединения, потому что вам не нужно иметь открытое соединение, как в Stream Sockets — вы создаете пакет с информацией о получателе и отправляете его. Они используют UDP (протокол пользовательских дейтаграмм).
Необработанные сокеты. Предоставляют пользователям доступ к базовым протоколам связи, которые поддерживают абстракции сокетов. Эти сокеты обычно ориентированы на дейтаграммы, хотя их точные характеристики зависят от интерфейса, предоставляемого протоколом. Необработанные сокеты не предназначены для обычного пользователя; они были предоставлены в основном тем, кто заинтересован в разработке новых протоколов связи или для получения доступа к некоторым наиболее загадочным возможностям существующего протокола.
Последовательные пакетные сокеты. Они похожи на потоковый сокет, за исключением того, что границы записи сохраняются.Этот интерфейс предоставляется только как часть абстракции сокетов сетевых систем (NS) и очень важен в большинстве серьезных приложений NS. Сокеты с последовательными пакетами позволяют пользователю манипулировать заголовками протокола последовательного пакета (SPP) или протокола дейтаграмм Интернета (IDP) в пакете или группе пакетов либо путем записи прототипа заголовка вместе с любыми данными, которые должны быть отправлены, либо путем указание заголовка по умолчанию, который будет использоваться со всеми исходящими данными, и позволяет пользователю получать заголовки входящих пакетов.
Что дальше?
Следующие несколько глав предназначены для укрепления ваших основ и подготовки фундамента, прежде чем вы сможете писать серверные и клиентские программы с использованием socket. Если вы хотите сразу перейти к написанию клиентской и серверной программы, вы можете это сделать, но это не рекомендуется. Настоятельно рекомендуется пройтись шаг за шагом и завершить несколько начальных глав, чтобы создать базу, прежде чем переходить к программированию.
Может ли кто-нибудь объяснить мне, что такое сокет? Я вижу это во многих аббревиатурах в контексте SSL и т. д.
Кроме того, почему он называется сокетом? Только потому, что это имя они придумали? Или это первое название, которое они придумали?
С точки зрения непрофессионала: розетка — это телефон. Это то, что вы держите в руке, что позволяет вам разговаривать с другим телефоном. Аналогия немного нарушается: большинство телефонных разговоров являются одноранговыми. Сокетные соединения клиент-сервер. Клиент (такой как, помимо прочего, программное обеспечение рабочей станции, такое как браузеры) подключается к серверу (например, веб-серверу, файловому серверу, серверу аутентификации или другому). Еще один изъян в аналогии: когда вы закрываете сокетное соединение, сокет уничтожается, и вы должны создать новый сокет, прежде чем сможете установить новое соединение.
Это не так уж плохо для аналогии. Сервер — это всего лишь колл-центр, на котором одновременно могут приниматься сотни активных вызовов.
6 ответов 6
Сокет – это псевдофайл, представляющий сетевое подключение. После создания сокета (с указанием другого хоста и порта) записи в этот сокет преобразуются в сетевые пакеты, которые отправляются наружу, а данные, полученные из сети, могут быть прочитаны из сокета.
Сокеты похожи на трубы. Оба выглядят как файлы для программ, которые их используют. Оба облегчают межпроцессное взаимодействие. Каналы взаимодействуют с локальной программой; сокеты взаимодействуют с удаленной программой. Сокеты также обеспечивают, как вы упомянули, двустороннюю связь (во многом как пара правильно соединенных каналов).
Наконец, программы на одном компьютере обычно взаимодействуют с использованием стандартных сетевых протоколов, таких как TCP; было бы расточительно проделывать весь путь к сетевому оборудованию (если оно есть!), вычислять контрольные суммы и т. д., чтобы вернуться на тот же хост. Сокеты домена Unix справляются с этим случаем. Они соединяют процессы на одном хосте, а не удаленные процессы, в обход сети.
Как упоминал Tripleee, в ходе истории BSD каналы были введены раньше, чем сокеты, и были повторно реализованы с использованием сокетов, когда они уже существовали. В том же справочнике, Дизайн и реализация операционной системы FreeBSD, упоминается, что каналы были затем возвращены к реализации без сокетов из соображений производительности.
Можно также упомянуть, что каналы появились раньше, чем сокеты, но как только интерфейс сокетов был добавлен в Unix, имело смысл повторно реализовать каналы с использованием локальных сокетов.
Быстрый поиск в Google открывает страницу 40 в Проектирование и реализация операционной системы FreeBSD; в тексте упоминается это изменение в 4.2BSD, но также поясняется, что это больше не делается по соображениям производительности.
Сокет — это просто логическая конечная точка для связи. Они существуют на транспортном уровне. Вы можете отправлять и получать вещи через сокет, вы можете привязываться к сокету и слушать его. Сокет относится к протоколу, машине и порту и указывается в заголовке пакета.
Руководства Beej по сетевому программированию и межпроцессному взаимодействию содержат полезную информацию о том, как использовать сокеты, и даже дают точный ответ на этот вопрос.
Итак, что это?
Сокет или "сокет" может иметь несколько значений:
Прежде всего, это модель мышления и интерфейс прикладного программирования (API). Это означает, что у вас есть набор правил, которым вы должны следовать, и набор функций, которые вы можете использовать для написания программ, которые что-то делают в соответствии с точно определенным контрактом. В данном конкретном случае что-то означает обмен данными с другой программой.
API сокетов широко абстрагируется от деталей "общения" в целом. Он инкапсулирует, с кем и как вы разговариваете, через одну (почти) непротиворечивую и идентичную шаблонную форму.
Вы можете создавать сокеты в разных «доменах» (например,«сокет unix» или «интернет-сокет») и разных типов связи (например, сокет «датаграмма» или сокет «поток») и общаться с разными получателями, и все работает точно так же (ну, 99%, очевидно, есть незначительные различия, которые вам придется учитывать).
Вам не нужно знать (и вы даже не хотите знать!), общаетесь ли вы с другой программой на том же компьютере или на другом компьютере, есть ли между этими компьютерами сеть IPv4 или IPv6, или возможно, какой-то другой протокол, о котором вы никогда не слышали.
socket — это также имя библиотечной функции (или системного вызова), которая создает "сокет", представляющий собой файл особого типа (все в Unix является файлом).
Как это сравнить с.
сокеты относятся к той же категории, что и каналы и каналы имен
Канал — это средство односторонней связи между читателем и писателем (оба являются программами) на одном компьютере. Он имитирует поток данных (как, например, TCP).
То есть с точки зрения канала не существует отдельных "сообщений" или "блоков данных". Вы можете скопировать любое количество данных на «один конец», и кто-то другой может прочитать любое количество данных (не обязательно то же самое и не обязательно за один раз) на «другом конце» в том же порядке байтов, что и вы. вставил его.
Именованный канал — это просто канал, который владеет именем в файловой системе. То есть это то, что выглядит и ведет себя точно так же, как файл, оно отображается в списке каталогов, и вы можете открыть его, записать в него и т. д. Обратите внимание, что вы также можете создавать специальные файлы сокетов (это будет именованный сокет) .
Сокет, с другой стороны, является средством двусторонней ("дуплексной") связи, что означает, что вы можете писать и читать из одного и того же сокета, и вам не нужны два отдельных сокета для двусторонней связи. коммуникация.
Кроме того, сокет может действовать как поток (идентично каналу), или он может отправлять дискретные, ненадежные сообщения, или может отправлять дискретные, упорядоченные сообщения (первые два работают на любом домене, последний только на «юникс-домен»). Он может отправлять сообщения (или имитировать поток) кому-то на совершенно другом компьютере. При некоторых условиях сокет может даже осуществлять связь "один ко многим" (многоадресная рассылка).
Имея это в виду, становится ясно, что сокеты делают нечто гораздо более сложное и обычно несут больше накладных расходов, чем конвейеры (которые, по сути, представляют собой не более чем простой memcpy в буфер и из него!), но если вы создаете локальные сокеты (т.е. на том же компьютере), операционная система обычно применяет сильно оптимизированный быстрый путь, так что особой разницы нет.
взаимодействие между процессами иногда упоминается в связи с сетями
Да, сокеты — это один из возможных способов межпроцессного взаимодействия (разделяемая память и конвейеры — примеры альтернатив). В то же время они используются для «сетевого взаимодействия», как объяснялось выше.
Глубокое погружение в то, что скрывается за ядром Linux, когда вы можете использовать системный вызов socket
Чиро С. Коста, 16 октября 2018 г.
Если вы уже некоторое время работаете с веб-серверами, вы наверняка уже сталкивались с классическим «адрес уже используется» (EADDRINUSE).
Здесь, в этой статье, мы не только рассмотрим, как увидеть, происходит ли такое условие, как обусловленное (просматривая список открытых сокетов), но также и проверим в фактических путях кода ядра, где эта проверка происходит. р>
Если вам интересно, как работает системный вызов socket(2), где хранятся эти сокеты, обязательно дочитайте до конца!
Это шестая статья в серии из 30 статей о procfs: Месяц /proc.
Если вы хотите быть в курсе последних новостей, обязательно присоединитесь к списку рассылки!
Для чего нужны эти сокеты?
Сокеты — это конструкции, которые позволяют процессам на разных машинах взаимодействовать через базовую сеть, а также могут использоваться как способ связи с другими процессами на том же хосте (через сокеты Unix).
Аналогия, которая действительно запомнилась мне, представлена в книге «Компьютерные сети: нисходящий подход».
На очень высоком уровне мы можем думать о сервере как об этом "доме" с набором дверей.
Поскольку каждая дверь соответствует розетке, клиент может подойти к двери дома и «постучаться» в нее.
Сразу после стука (отправки пакета SYN) дом автоматически отвечает ответом ( SYN+ACK ), который затем подтверждается домом (да, умный дом с «умной дверью»). р>
Между тем, пока процесс просто сидит в доме, клиенты организуются «умным домом», который создает две очереди: одну для тех, кого дом еще здоровается, и другую для тех, кого приветствие закончилось. .
Всякий раз, когда новые клиенты попадают во вторую очередь, процесс может позволить им войти.
Как только это соединение будет принято (клиенту будет предложено войти), сервер сможет взаимодействовать с ним, передавая и получая данные по желанию.
Одна деталь, которую следует отметить, заключается в том, что клиент на самом деле не «входит» — сервер создает «частную дверь» в доме (клиентский сокет), а затем связывается с клиентом оттуда.
Если вы хотите пошагово реализовать TCP-сервер на C, обязательно ознакомьтесь с этой статьей! Внедрение TCP-сервера.
Где искать список сокетов в моей системе?
Имея ментальную модель того, как выглядит установление TCP-соединения, мы теперь можем «попасть в дом» и изучить, как машина создает эти «двери» (розетки), сколько дверей в нашем доме и в каких из них. укажите, что они (закрыты? открыты?).
Для этого рассмотрим пример сервера, который просто создает сокет (дверь!) и ничего с ним не делает.
Под капотом такой простой системный вызов запускает целую кучу внутренних методов (подробнее об этом в следующем сеансе), которые в какой-то момент позволяют нам искать информацию об активных сокетах в трех разных файлах: /proc/< /p>
В то время как каталог fd представляет нам список файлов, открытых процессом, /proc/
Файл/net/tcp предоставляет нам информацию об активных TCP-соединениях (в их различных состояниях) в пространстве имен процесса network. sockstat, с другой стороны, больше похож на сводку.
Начиная с каталога fd, мы видим, что после вызова socket(2) мы видим файловый дескриптор сокета в списке файловых дескрипторов:
Учитывая, что при простом вызове socket(2) у нас нет TCP-подключения, из /proc/ невозможно получить соответствующую информацию.
Из сводки ( sockstat ) мы можем предположить, что мы увеличиваем количество выделенных сокетов TCP:
Чтобы убедиться, что мы действительно увеличиваем количество аллоков, мы можем изменить приведенный выше исходный код и вместо этого выделить 100 сокетов:
Теперь, проверив это еще раз, мы видим, что alloc имеет более высокий номер:
Теперь вопрос в том, как внутри создается сокет?
Что происходит внутри, когда вызывается системный вызов сокета?
socket(2) — это своего рода фабрика, производящая базовые структуры для обработки операций с таким сокетом.
Используя iovisor/bcc , мы можем отследить самый глубокий вызов, который происходит в стеке вызовов sys_socket, и оттуда понять каждый шаг.
Начиная с самого sys_socket, эта оболочка системного вызова — это первое, что нужно затронуть в пространстве ядра, отвечая за выполнение различных проверок и подготовку некоторых флагов для передачи последующим вызовам.
После того, как предварительные проверки были выполнены, он размещает в своем стеке указатель на структуру socket , структуру, которая в конечном итоге будет содержать информацию о сокете, не относящуюся к протоколу:
Учитывая, что в момент создания сокета мы можем выбирать между различными типами и семействами протоколов (например, UDP, UNIX и TCP), структура сокета содержит интерфейс ( struct proto_ops* ), определяющий основные конструкции, которые реализуют сокеты (независимо от их семейства/типа), которые инициируются в следующем вызываемом методе: sock_create .
Продолжая наше глубокое погружение, теперь мы можем внимательно рассмотреть, как фактический сокет структуры выделяется с помощью sock_alloc() .
Этот метод выделяет две вещи: новый inode и объект сокета.
Эти два компонента связаны друг с другом через файловую систему sockfs, которая отвечает не только за отслеживание информации о сокетах в системе, но и за обеспечение уровня перевода между обычными вызовами файловой системы (такими как write(2)) и сетевым стеком. (независимо от основного домена связи).
Отслеживая sock_alloc_inode, метод, отвечающий за выделение индексного дескриптора в sockfs, мы можем увидеть, как он настраивается:
Сокеты и лимиты ресурсов
Учитывая, что индексный дескриптор файловой системы может быть указан из пользовательского пространства из файлового дескриптора, после того, как все базовые структуры ядра настроены, sys_socket отвечает за создание файлового дескриптора для пользователя (проходя проверки ограничений ресурсов как представлены в разделе Внутренние ограничения ресурсов процесса.
Если вы задавались вопросом, почему вы можете получить сообщение об ошибке "слишком много открытых файлов" для socket(2), то причина в том, что он проходит те же проверки ограничения ресурсов:
Подсчет количества сокетов в системе
Если вы обратили внимание на вызов sock_alloc, часть его заботилась об увеличении количества «используемых» сокетов.
Поскольку this_cpu_add является макросом, мы можем посмотреть на его определение и узнать о нем немного больше:
Теперь, учитывая, что мы всегда добавляем к sockets_in_use , мы можем, по крайней мере, предположить, что если мы пройдем через метод, зарегистрированный для /proc/net/sockstat, он будет использовать это значение, что он и делает (с также выполняя добавление значений, зарегистрированных для каждого процессора):
А как насчет пространств имен?
Как вы могли заметить, в коде, связанном с пространствами имен, нет логики, когда речь идет о подсчете количества выделенных сокетов.
Поначалу это меня очень удивило, учитывая, что я думал, что сетевой стек — это то, что имеет наибольшее пространство имен, но оказалось, что есть еще некоторые моменты, которых нет.
Если вы хотите убедиться в этом самостоятельно, прочтите статью Использование сетевых пространств имен и виртуального коммутатора для изоляции серверов.
Суть в том, что вы можете создать кучу сокетов, увидеть sockstat , затем создать сетевое пространство имен, войти в него, а затем увидеть, что хотя вы не можете видеть там TCP-сокеты всей системы ( пространства имен в действии!), вы можете увидеть общее количество выделенных сокетов в системе (без пространства имен).
Заключительные мысли
Интересно наблюдать, как после изучения внутренней работы ядра простое любопытство к /proc приводит к ответам на вопрос, почему некоторые конкретные действия, которые я видел в повседневных операциях, работают таким образом.
Учитывая, что это только первая статья о /proc/net, и я уже многому научился, мне не терпится начать копать глубже в остальную часть!
Если вы хотите следовать за мной, обязательно подпишитесь на список рассылки.
Если у вас есть какие-либо вопросы или мысли, которыми вы хотели бы поделиться, дайте мне знать!
Меня зовут cirowrc в Твиттере, и я бы хотел с вами пообщаться!
Хорошего настроения, Чиро
Ресурсы
Рекомендуемые статьи
Если вы узнали что-то новое из этой статьи, вы можете воспользоваться и другими!
Оставайтесь на связи!
Время от времени я буду предоставлять вам контент.
Сообщения электронной почты не рассылаются автоматически. В них содержится информация о вещах, которыми, по моему мнению, стоит поделиться и которые лично я хотел бы получать.
Это третья и последняя статья из серии о межпроцессном взаимодействии (IPC) в Linux. Первая статья была посвящена IPC через общее хранилище (файлы и сегменты памяти), а вторая статья делает то же самое для основных каналов: каналов (именованных и неименованных) и очередей сообщений. В этой статье мы переходим от IPC на верхнем уровне (сокеты) к IPC на нижнем уровне (сигналы). Примеры кода раскрывают детали.
Сокеты
Подобно тому, как каналы бывают двух видов (именованные и неименованные), так же и сокеты. Сокеты IPC (также известные как сокеты домена Unix) обеспечивают связь на основе каналов для процессов на одном физическом устройстве (хост), в то время как сетевые сокеты позволяют использовать этот тип IPC для процессов, которые могут выполняться на разных хостах, тем самым обеспечивая сети в игру. Сетевые сокеты нуждаются в поддержке базового протокола, такого как TCP (протокол управления передачей) или UDP нижнего уровня (протокол пользовательских дейтаграмм).
Напротив, сокеты IPC полагаются на ядро локальной системы для поддержки связи; в частности, сокеты IPC обмениваются данными, используя локальный файл в качестве адреса сокета. Несмотря на эти различия в реализации, API-интерфейсы сокетов IPC и сетевых сокетов в основном одинаковы. Предстоящий пример охватывает сетевые сокеты, но примеры серверной и клиентской программ могут работать на одном и том же компьютере, поскольку сервер использует сетевой адрес localhost (127.0.0.1), адрес локальной машины на локальной машине. .
Сокеты, сконфигурированные как потоки (обсуждаемые ниже), являются двунаправленными, а управление осуществляется по шаблону клиент/сервер: клиент инициирует диалог, пытаясь подключиться к серверу, который пытается принять соединение. Если все работает, запросы от клиента и ответы от сервера могут проходить по каналу до тех пор, пока он не будет закрыт с обеих сторон, что приведет к разрыву соединения.
[Загрузить полное руководство по межпроцессному взаимодействию в Linux]
Сервер итеративный, который подходит только для разработки, обрабатывает подключенных клиентов по одному до завершения: первый клиент обрабатывается от начала до конца, затем второй и так далее. Недостатком является то, что обработка конкретного клиента может зависнуть, что приведет к голоданию всех клиентов, ожидающих позади. Сервер производственного уровня будет параллельным, обычно использующим некоторое сочетание многопроцессорной обработки и многопоточности. Например, веб-сервер Nginx на моем настольном компьютере имеет пул из четырех рабочих процессов, которые могут одновременно обрабатывать клиентские запросы.Следующий пример кода сводит беспорядок к минимуму за счет использования итеративного сервера; таким образом, основное внимание уделяется базовому API, а не параллелизму.
Наконец, API сокетов со временем претерпел значительные изменения по мере появления различных усовершенствований POSIX. Текущий пример кода для сервера и клиента преднамеренно прост, но подчеркивает двунаправленный аспект соединения через сокет на основе потока. Вот краткая информация о потоке управления, когда сервер запускается в терминале, а клиент запускается в отдельном терминале:
- Сервер ожидает клиентских подключений и, при успешном подключении, считывает байты от клиента.
- Чтобы подчеркнуть двусторонний диалог, сервер возвращает клиенту байты, полученные от клиента. Эти байты представляют собой коды символов ASCII, из которых состоят названия книг.
- Клиент записывает названия книг в серверный процесс, а затем считывает те же названия, которые возвращаются с сервера. И сервер, и клиент выводят заголовки на экран. Вот вывод сервера, практически такой же, как у клиента:
Пример 1. Сервер сокетов
Приведенная выше серверная программа выполняет классические четыре шага, чтобы подготовиться к клиентским запросам, а затем принять отдельные запросы. Каждый шаг назван в честь системной функции, которую вызывает сервер:
- socket(…): получить дескриптор файла для подключения к сокету
- bind(…): привязать сокет к адресу на хосте сервера
- listen(…): слушать запросы клиентов
- accept(…): принять конкретный запрос клиента
Полный вызов сокета:
Первый аргумент указывает сетевой сокет, а не сокет IPC. Существует несколько вариантов второго аргумента, но чаще всего используются SOCK_STREAM и SOCK_DGRAM (датаграмма). Сокет на основе потока поддерживает надежный канал, по которому сообщаются потерянные или измененные сообщения; канал является двунаправленным, и полезная нагрузка от одной стороны к другой может быть произвольной по размеру. Напротив, сокет на основе дейтаграмм ненадежен (лучшая попытка), однонаправлен и требует полезной нагрузки фиксированного размера. Третий аргумент socket определяет протокол. Для используемого здесь потокового сокета есть единственный выбор, который представляет ноль: TCP. Поскольку успешный вызов socket возвращает знакомый файловый дескриптор, сокет записывается и читается с тем же синтаксисом, что и, например, локальный файл.
Вызов привязки является наиболее сложным, так как он отражает различные усовершенствования в API сокетов. Интересно, что этот вызов привязывает сокет к адресу памяти на сервере. Однако вызов прослушивания прост:
Первый аргумент – это файловый дескриптор сокета, а второй указывает, сколько клиентских подключений может быть выполнено, прежде чем сервер выдаст ошибку отказ в подключении при попытке подключения. (MaxConnects имеет значение 8 в файле заголовка sock.h.)
Вызов accept по умолчанию использует ожидание блокировки: сервер ничего не делает, пока клиент не попытается подключиться, а затем продолжит работу. Функция accept возвращает -1, чтобы указать на ошибку. Если вызов завершается успешно, он возвращает другой файловый дескриптор — для сокета чтение/запись, в отличие от сокета принятия, на который ссылается первый аргумент в вызове accept. Сервер использует сокет чтения/записи для чтения запросов от клиента и записи ответов обратно. Принимающий сокет используется только для приема клиентских подключений.
По замыслу сервер работает бесконечно. Соответственно, сервер можно остановить с помощью Ctrl+C из командной строки.
Пример 2. Клиент сокета
Код установки клиентской программы аналогичен коду установки сервера. Основное различие между ними заключается в том, что клиент не слушает и не принимает, а вместо этого подключается:
Вызов соединения может завершиться ошибкой по нескольким причинам. например, у клиента неправильный адрес сервера или к серверу уже подключено слишком много клиентов. Если операция подключения завершается успешно, клиент записывает запросы, а затем считывает отраженные ответы в цикле for. После диалога и сервер, и клиент закрывают сокет чтения/записи, хотя операции закрытия с любой стороны достаточно, чтобы закрыть соединение. После этого клиент завершает работу, но, как отмечалось ранее, сервер остается открытым для работы.
Пример с сокетом, в котором сообщения запроса возвращаются обратно клиенту, намекает на возможности произвольного насыщенного диалога между сервером и клиентом. Возможно, это главная привлекательность сокетов. В современных системах клиентские приложения (например, клиент базы данных) обычно взаимодействуют с сервером через сокет. Как отмечалось ранее, локальные сокеты IPC и сетевые сокеты отличаются лишь несколькими деталями реализации; в общем, сокеты IPC имеют меньшие накладные расходы и лучшую производительность.Коммуникационный API практически одинаков для обоих.
Сигналы
сигнал прерывает выполняющуюся программу и в этом смысле взаимодействует с ней. Большинство сигналов можно либо игнорировать (заблокировать), либо обрабатывать (с помощью назначенного кода), за исключением SIGSTOP (пауза) и SIGKILL (немедленное завершение). Символьные константы, такие как SIGKILL, имеют целые значения, в данном случае 9.
Сигналы могут возникать при взаимодействии с пользователем. Например, пользователь нажимает Ctrl+C из командной строки, чтобы завершить программу, запущенную из командной строки; Ctrl+C генерирует сигнал SIGTERM. SIGTERM для завершения, в отличие от SIGKILL, может быть либо заблокирован, либо обработан. Один процесс также может сигнализировать другому, тем самым превращая сигналы в механизм IPC.
Рассмотрите возможность корректного завершения работы многопроцессорного приложения, такого как веб-сервер Nginx, из другого процесса. Функция уничтожения:
может использоваться одним процессом для завершения другого процесса или группы процессов. Если первый аргумент функции kill больше нуля, этот аргумент рассматривается как pid (идентификатор процесса) целевого процесса; если аргумент равен нулю, он идентифицирует группу процессов, к которой принадлежит отправитель сигнала.
Вторым аргументом kill является либо стандартный номер сигнала (например, SIGTERM или SIGKILL), либо 0, что заставляет вызов сигнализировать о запросе о том, действительно ли pid в первом аргументе действителен. . Таким образом, корректное завершение многопроцессорного приложения может быть выполнено путем отправки сигнала terminate — вызова функции уничтожения с SIGTERM в качестве второго аргумента — группе процессов, составляющих приложение. (Главный процесс Nginx может завершать рабочие процессы с помощью вызова kill, а затем завершать сам себя.) Функция kill, как и многие библиотечные функции, заключает в себе силу и гибкость простого синтаксиса вызова.
Пример 3. Грамотное завершение работы многопроцессорной системы
Приведенная выше программа shutdown имитирует плавное завершение работы многопроцессорной системы, в данном случае простой, состоящей из родительского процесса и одного дочернего процесса. Моделирование работает следующим образом:
- Родительский процесс пытается разветвить дочерний процесс. Если ответвление завершается успешно, каждый процесс выполняет свой собственный код: дочерний процесс выполняет функцию child_code, а родительский процесс выполняет функцию parent_code.
- Дочерний процесс входит в потенциально бесконечный цикл, в котором дочерний процесс засыпает на секунду, печатает сообщение, снова засыпает и так далее. Именно сигнал SIGTERM от родителя заставляет дочерний элемент изящно выполнять функцию обратного вызова, обрабатывающую сигнал. Таким образом, сигнал вырывает дочерний процесс из его цикла и устанавливает корректное завершение как дочернего, так и родительского процессов. Дочерний элемент печатает сообщение перед завершением.
- Родительский процесс после разветвления дочернего процесса приостанавливается на пять секунд, чтобы дочерний процесс мог выполняться некоторое время; конечно, ребенок в основном спит в этой симуляции. Затем родитель вызывает функцию уничтожения с SIGTERM в качестве второго аргумента, ждет, пока дочерний процесс завершится, а затем завершает работу.
Вот результат пробного запуска:
Для обработки сигналов в примере используется библиотечная функция sigaction (рекомендуется POSIX), а не устаревшая функция signal, которая имеет проблемы с переносимостью. Вот сегменты кода, представляющие наибольший интерес:
-
Если вызов fork завершается успешно, родитель выполняет функцию parent_code, а дочерний — функцию child_code. Родитель ждет пять секунд, прежде чем сигнализировать ребенку:
Если вызов kill успешен, родитель ожидает завершения дочернего процесса, чтобы предотвратить превращение дочернего процесса в постоянного зомби; после ожидания родитель завершает работу.
Первые три строки — подготовка. Четвертый оператор устанавливает обработчик в функцию грациозно, которая печатает некоторые сообщения перед вызовом _exit для завершения. Затем пятый и последний оператор регистрирует обработчик в системе посредством вызова sigaction. Первый аргумент для sigaction — SIGTERM для terminate, второй — текущая настройка sigaction, а последний аргумент (в данном случае NULL) может использоваться для сохранения предыдущей настройки sigaction, возможно, для последующего использования.
Использование сигналов для IPC — это действительно минималистский подход, но проверенный временем. IPC через сигналы явно принадлежит набору инструментов IPC.
Подведение итогов
В этих трех статьях о IPC на примерах кода рассмотрены следующие механизмы:
- Общие файлы
- Общая память (с семафорами)
- Каналы (именованные и неименованные)
- Очереди сообщений
- Сокеты
- Сигналы
Конечно, нет простого ответа на вопрос, какой из механизмов IPC является лучшим.Каждый из них предполагает компромисс, типичный для программирования: простота и функциональность. Сигналы, например, представляют собой относительно простой механизм межпроцессного взаимодействия, но не поддерживают обмен данными между процессами. Если такое преобразование необходимо, то один из других вариантов является более подходящим. Общие файлы с блокировкой достаточно просты, но общие файлы могут работать недостаточно хорошо, если процессам необходимо совместно использовать большие потоки данных; каналы или даже сокеты с более сложными API могут быть лучшим выбором. Позвольте проблеме определить выбор.
Хотя пример кода (доступен на моем веб-сайте) полностью написан на C, другие языки программирования часто предоставляют тонкие оболочки для этих механизмов IPC. Надеюсь, примеры кода короткие и достаточно простые, чтобы побудить вас к экспериментам.
Читайте также: