2011. Машбук (1114722), страница 49
Текст из файла (страница 49)
для того, чтобы он мог войти в критическую секцию, значение семафорадолжно стать нулевым; второй процесс блокируется при попытке уменьшить значениесемафора до отрицательной величины, для того, чтобы этот процесс мог войти вкритическую секцию, значение семафора должно быть не менее 3. Обратим внимание, чтов данном примере, помимо взаимного исключения процессов, достигается строгаяпоследовательность действий двух процессов: они получают доступ к критической секциистрого по очереди.1823.3Сокеты — унифицированный интерфейспрограммирования распределенных системСредства межпроцессного взаимодействия ОС UNIX, представленные в системеIPC, решают проблему взаимодействия процессов, выполняющихся в рамках однойоперационной системы.
Однако, очевидно, их невозможно использовать, когда требуетсяорганизовать взаимодействие процессов в рамках сети. Это связано как с принятойсистемой именования, которая обеспечивает уникальность только в рамках даннойсистемы, так и вообще с реализацией механизмов разделяемой памяти, очередисообщений и семафоров, – очевидно, что для удаленного взаимодействия они не годятся.Следовательно, возникает необходимость в каком-то дополнительном механизме,позволяющем общаться двум процессам в рамках сети. Однако если разработчикипрограмм будут иметь два абсолютно разных подхода к реализации взаимодействияпроцессов, в зависимости от того, на одной машине они выполняются или на разных узлахсети, им, очевидно, придется во многих случаях создавать два принципиально разныхкуска кода, отвечающих за это взаимодействие. Понятно, что это неудобно и хотелось быв связи с этим иметь некоторый унифицированный механизм, который в определеннойстепени позволял бы абстрагироваться от расположения процессов и давал бывозможность использования одних и тех же подходов для локального и нелокальноговзаимодействия.
Кроме того, как только мы обращаемся к сетевому взаимодействию,встает проблема многообразия сетевых протоколов и их использования. Понятно, чтобыло бы удобно иметь какой-нибудь общий интерфейс, позволяющий пользоватьсяуслугами различных протоколов по выбору пользователя.Обозначенные проблемы был призван решить механизм, впервые появившийся вUNIX – BSD (4.2) и названный сокетами (sockets).Сокеты представляют собой в определенном смысле обобщение механизмаканалов, но с учетом возможных особенностей, возникающих при работе в сети. Крометого, они предоставляют больше возможностей по передаче сообщений, например, могутподдерживать передачу экстренных сообщений вне общего потока данных. Общая схемаработы с сокетами любого типа такова: каждый из взаимодействующих процессов долженна своей стороне создать и отконфигурировать сокет, после чего процессы должныосуществить соединение с использованием этой пары сокетов.
По окончаниивзаимодействия сокеты уничтожаются.Механизм сокетов чрезвычайно удобен при разработке взаимодействующихприложений, образующих систему «клиент-сервер». Клиент посылает серверу запросы напредоставление услуги, а сервер отвечает на эти запросы.Схема использования механизма сокетов для взаимодействия в рамках модели«клиент-сервер» такова. Процесс-сервер запрашивает у ОС сокет и, получив его,присваивает ему некоторое имя (адрес), которое предполагается заранее известным всемклиентам, которые захотят общаться с данным сервером. После этого сервер переходит врежим ожидания и обработки запросов от клиентов.
Клиент, со своей стороны, тожесоздает сокет и запрашивает соединение своего сокета с сокетом сервера, имеющимизвестное ему имя (адрес). После того, как соединение будет установлено, клиент и сервермогут обмениваться данными через соединенную пару сокетов. Ниже мы подробнорассмотрим функции, выполняющие все необходимые действия с сокетами, и напишемпример небольшой серверной и клиентской программы, использующих сокеты.3.3.1 Типы сокетов.
Коммуникационный доменСокеты подразделяются на несколько типов в зависимости от типакоммуникационного соединения, который они используют. Два основных типа183коммуникационных соединений и, соответственно, сокетов представляет собойсоединение с использованием виртуального канала и датаграммное соединение.Соединение с использованием виртуального канала – это последовательный потокбайтов, гарантирующий надежную доставку сообщений с сохранением порядка ихследования.
Данные начинают передаваться только после того, как виртуальный каналустановлен, и канал не разрывается, пока все данные не будут переданы. Примеромсоединения с установлением виртуального канала является механизм каналов в UNIX,аналогом такого соединения из реальной жизни также является телефонный разговор.Заметим, что границы сообщений при таком виде соединений не сохраняются, т.е.приложение, получающее данные, должно само определять, где заканчивается односообщение и начинается следующее. Такой тип соединения может также поддерживатьпередачу экстренных сообщений вне основного потока данных, если это возможно прииспользовании конкретного выбранного протокола.Датаграммное соединение используется для передачи отдельных пакетов,содержащих порции данных – датаграмм.
Для датаграмм не гарантируется доставка в томже порядке, в каком они были посланы. Вообще говоря, для них не гарантируетсядоставка вообще, надежность соединения в этом случае ниже, чем при установлениивиртуального канала. Однако датаграммные соединения, как правило, более быстрые.Примером датаграммного соединения из реальной жизни может служить обычная почта:письма и посылки могут приходить адресату не в том порядке, в каком они были посланы,а некоторые из них могут и совсем пропадать.Поскольку сокеты могут использоваться как для локального, так и для удаленноговзаимодействия, встает вопрос о пространстве адресов сокетов.
При создании сокетауказывается так называемый коммуникационный домен, к которому данный сокет будетпринадлежать. Коммуникационный домен определяет конкретную модель именования(форматы адресов, правила их интерпретации), а также область взаимодействияпроцессов. Мы будем рассматривать два основных домена: для локальноговзаимодействия – домен AF_UNIX и для взаимодействия в рамках сети – домен AF_INET(префикс AF обозначает сокращение от «address family» – семейство адресов). В доменеAF_UNIX формат адреса – это допустимое имя файла, в домене AF_INET адрес образуютимя хоста + номер порта (порт – виртуальная точка соединения, которая позволяетадресовать конкретный процесс извне).Заметим, что фактически коммуникационный домен определяет такжеиспользуемые семейства протоколов.
Так, для домена AF_UNIX это будут внутренниепротоколы ОС, для домена AF_INET – протоколы семейства TCP/IP. Современныесистемы поддерживают и другие коммуникационные домены, например BSD UNIXподдерживает также третий домен – AF_NS, использующий протоколы удаленноговзаимодействия Xerox NS.Ниже приведен набор функций для работы с сокетами.3.3.2 Создание и конфигурирование сокета3.3.2.1 Создание сокета#include <sys/types.h>#include <sys/socket.h>int socket (int domain, int type, int protocol)Функция создания сокета так и называется – socket().
У нее имеется триаргумента. Первый аргумент – domain – обозначает коммуникационный домен, ккоторому должен принадлежать создаваемый сокет. Для двух рассмотренных намидоменов соответствующие константы будут равны, как мы уже говорили, AF_UNIX иAF_INET. Второй аргумент – type – определяет тип соединения, которым будет184пользоваться сокет (и, соответственно, тип сокета).
Для двух основных рассматриваемыхнами типов сокетов это будут константы SOCK_STREAM для соединения с установлениемвиртуального канала и SOCK_DGRAM для датаграмм6. Третий аргумент – protocol –задает конкретный протокол, который будет использоваться в рамках данногокоммуникационного домена для создания соединения. Если установить значение данногоаргумента в 0, система автоматически выберет подходящий протокол. В наших примерахмы так и будем поступать. Однако здесь для справки приведем константы для протоколов,используемых в домене AF_INET:IPPROTO_TCP – обозначает протокол TCP (корректно при создании сокета типаSOCK_STREAM)IPPROTO_UDP – обозначает протокол UDP (корректно при создании сокета типаSOCK_DGRAM)Функция socket() возвращает в случае успеха положительное целое число –дескриптор сокета, которое может быть использовано в дальнейших вызовах при работе сданным сокетом.
Заметим, что дескриптор сокета фактически представляет собойфайловый дескриптор, а именно, он является индексом в таблице файловых дескрипторовпроцесса, и может использоваться в дальнейшем для операций чтения и записи в сокет,которые осуществляются подобно операциям чтения и записи в файл (подробно этиоперации будут рассмотрены ниже).В случае если создание сокета с указанными параметрами невозможно (например,при некорректном сочетании коммуникационного домена, типа сокета и протокола),функция возвращает –1.3.3.2.2 СвязываниеДля того чтобы к созданному сокету мог обратиться какой-либо процесс извне,необходимо связать с этим сокетом некоторое имя (адрес).
Как мы уже говорили, форматадреса зависит от коммуникационного домена, в рамках которого действует сокет, иможет представлять собой либо путь к файлу, либо сочетание IP-адреса и номера порта.Но в любом случае связывание сокета с конкретным адресом осуществляется одной и тойже функцией bind:#include <sys/types.h>#include <sys/socket.h>int bind (int sockfd, struct sockaddr *myaddr, int addrlen)Первый аргумент функции – дескриптор сокета, возвращенный функциейsocket(); второй аргумент – указатель на структуру, содержащую адрес сокета. Длядомена AF_UNIX формат структуры описан в <sys/un.h> и выглядит следующимобразом:#include <sys/un.h>struct sockaddr_un {short sun_family; /* == AF_UNIX */char sun_path[108];};Для домена AF_INET формат структуры описан в <netinet/in.h> и выглядитследующим образом:#include <netinet/in.h>6Заметим, что данный аргумент может принимать не только указанные два значения, например, типсокета SOCK_SEQPACKET обозначает соединение с установлением виртуального канала со всемивытекающими отсюда свойствами, но при этом сохраняются границы сообщений; однако данный типсокетов не поддерживается ни в домене AF_UNIX, ни в домене AF_INET, поэтому мы его здесьрассматривать не будем185struct sockaddr_in {short sin_family; /* == AF_INET */u_short sin_port; /* port number */struct in_addr sin_addr; /* host IP address */char sin_zero[8]; /* not used */};Последний аргумент функции задает реальный размер структуры, на которуюуказывает myaddr.Важно отметить, что если мы имеем дело с доменом AF_UNIX и адрес сокетапредставляет собой имя файла, то при выполнении функции bind() система в качествепобочного эффекта создает файл с таким именем.
Поэтому для успешного выполненияbind() необходимо, чтобы такого файла не существовало к данному моменту. Этоследует учитывать, если мы «зашиваем» в программу определенное имя и намеренызапускать нашу программу несколько раз на одной и той же машине – в этом случае дляуспешной работы bind() необходимо удалять файл с этим именем перед связыванием.Кроме того, в процессе создания файла, естественно, проверяются права доступапользователя, от имени которого производится вызов, ко всем директориям,фигурирующим в полном путевом имени файла, что тоже необходимо учитывать призадании имени. Если права доступа к одной из директорий недостаточны, вызов bind()завершится неуспешно.В случае успешного связывания bind() возвращает 0, в случае ошибки – -1.3.3.3 Предварительное установление соединения.3.3.3.1 Сокеты с установлением соединения. Запрос на соединение.Различают сокеты с предварительным установлением соединения, когда доначала передачи данных устанавливаются адреса сокетов отправителя и получателяданных – такие сокеты соединяются друг с другом и остаются соединенными доокончания обмена данными; и сокеты без установления соединения, когда соединениедо начала передачи данных не устанавливается, а адреса сокетов отправителя иполучателя передаются с каждым сообщением.