Курынин Р.В., Машечкин И.В., Терехин А.Н. - Конспект лекций по ОС (1114685), страница 40
Текст из файла (страница 40)
Однако, очевидно, их невозможно использовать, когда требуется организоватьвзаимодействие процессов в рамках сети. Это связано как с принятой системой именования,которая обеспечивает уникальность только в рамках данной системы, так и вообще с реализациеймеханизмов разделяемой памяти, очереди сообщений и семафоров, — очевидно, что дляудаленного взаимодействия они не годятся.
Следовательно, возникает необходимость в каком-тодополнительном механизме, позволяющем общаться двум процессам в рамках сети. При этоммеханизм должен быть унифицированным: он должен в определенной степени позволятьабстрагироваться от расположения процессов и давать возможность использования одних и тех жеподходов для локального и нелокального взаимодействия. Кроме того, как только мы обращаемсяк сетевому взаимодействию, встает проблема многообразия сетевых протоколов и ихиспользования. Понятно, что было бы удобно иметь какой-нибудь общий интерфейс,позволяющий пользоваться услугами различных протоколов по выбору пользователя.Обозначенные проблемы был призван решить механизм, впервые появившийся вБерклиевском UNIX — BSD, начиная с версии 4.2, и названный сокетами (sockets).
Нижеподробно рассматривается этот механизм.Механизм сокетов обеспечивает два типа соединений. Во-первых, это соединение,обеспечивающее установление виртуального канала (т.е. обеспечиваются соответствующиесвойства, в частности, гарантируется порядок передачи сообщения), его прямым аналогомявляется протокол TCP. Во-вторых, это дейтаграммное соединение (соответственно, безобеспечения порядка передачи и т.п.), аналогом которого является протокол UDP.Именование сокетов для организации работы с ними определяется т.н.коммуникационным доменом. Аппарат сокетов в общем случае поддерживает целый спектркоммуникационных доменов, среди которых нас будут интересовать два из них: домен AF_UNIX(семейство имен в ОС Unix) и AF_INET (семейство имен для сети Internet, определяемое стекомпротоколов TCP/IP).Для создания сокета используется системный вызов socket().#include <sys/types.h>#include <sys/socket.h>int socket(int domain, int type, int protocol);Первый параметр данного системного вызова определяет код коммуникационного домена.Коммуникационный домен, в свою очередь, определяет структуру именования, которая может162быть использована для сокета.
Как говорилось выше, на сегодняшний день существует целый ряддоменов, мы же остановимся на доменах, обозначаемых константами AF_UNIX и AF_INET.Второй параметр отвечает за тип сокета: либо SOCK_STREAM (тип виртуальный канал),либо SOCK_DGRAM (дейтаграммный тип сокета).Последний параметр вызова — протокол. Выбор значения данного параметра зависит отмногих факторов — и в первую очередь, от выбора коммуникационного домена и от выбора типасокета. Если указывается нулевое значение этого параметра, то система автоматически выберетпротокол, учитывая значения первых аргументов вызова. А можно указать константу, связанную сименем конкретного протокола: например, IPPROTO_TCP для протокола TCP (домена AF_INET)или IPPROTO_UDP для протокола UDP (домена AF_INET).
Но в последнем случае необходимоучесть, что могут возникать ошибочные ситуации. Например, если явно выбран домен AF_INET,тип сокета виртуальный канал и протокол UDP, то возникнет ошибка. Однако, если домен будеттем же, тип сокета дейтаграммный и протокол TCP, то ошибки не будет: просто дейтаграммноесоединение будет реализовано на выбранном протоколе.В случае успешного завершения системный вызов socket() возвращает открытый файловыйдескриптор, ассоциированный с созданным сокетом. Как отмечалось выше, сокеты представляютсобой особый вид файлов в файловой системе ОС Unix. Но данный дескриптор являетсялокальным атрибутом: это лишь номер строки в таблице открытых файлов текущего процесса, вкоторой появилась информация об этом открытом файле.
И им нельзя воспользоваться другимпроцессам, чтобы организовать взаимодействие с текущим процессом посредством данногосокета. Необходимо связать с этим сокетом некоторое имя, доступное другим процессам,посредством которого они смогут осуществлять взаимодействие. Для организации именованияиспользуется системный вызов bind().#include <sys/types.h>#include <sys/socket.h>int bind(int sockfd, struct sockaddr *myaddr, int addrlen);Посредством данного системного вызова возможно связывание файлового дескриптора(первого параметра), ассоциированного с открытым сокетом, с некоторой структурой, в которойбудет размещаться имя (или адрес) данного сокета. Для разных коммуникационных доменовструктура данного имени различна. Например, для домена AF_UNIX она выглядит следующимобразом:#include <sys/un.h>struct sockaddr_un{short sun_family; /* == AF_UNIX */char sun_path[108];};Первое поле в данной структуре — это код коммуникационного домена, а второе поле — этополное имя.
Для домена AF_INET структура выглядит несколько иначе:163#include <netinet/in.h>struct sockaddr_in{short sin_family;/* == AF_INET */u_short sin_port;/* номер порта */struct in_addr sin_addr; /* IP-адрес хоста */char sin_zero[8];/* не используется */};В данной структуре присутствует различная информация, в частности, IP-адрес, номер порта и т.п.И, наконец, последний аргумент addrlen рассматриваемого системного вызовахарактеризует размер структуры второго аргумента (т.е.
размер структуры sockaddr).В случае успешного завершения данный вызов возвращает значение 0, иначе — -1.Механизм сокетов включает в свой состав достаточно разнообразные средства,позволяющие организовывать взаимодействие различной топологии. В частности, имеетсявозможность организации взаимодействия с т.н. предварительным установлением соединения.Данная модель ориентирована на организацию клиент-серверных систем, когда организуется одинсерверный узел процессов (обратим внимание, что не процесс, а именно узел процессов), которыйпринимает сообщения от клиентский процессов и их как-то обрабатывает. Общая схема подобноговзаимодействия представлена ниже (3.3).
Заметим, что тип сокета в данном случае не важен, т.е.можно использовать сокеты, являющиеся виртуальными каналами, так и дейтаграммными.Серверный сокетsocket()Клиентский сокетbind()socket()listen()bind()accept()connect()send()recv()send()shutdown()recv()shutdown()close()close()Рис. 91.Схема работы с сокетами с предварительным установлением соединения.В данной модели можно выделить две группы процессов: процессы серверной части ипроцессы клиентской части. На стороне сервера открывается основной сокет. Посколькунеобходимо обеспечить возможность другим процессам обращаться к серверному сокету по164имени, то в данном случае необходимо связывание сокета с именем (вызов bind()). Затемсерверный процесс переводится в т.н.
режим прослушивания (посредством системного вызоваlisten()): это означает, что данный процесс может принимать запросы на соединение с ним отклиентских процессов. При этом, в вызове listen() оговаривается очередь запросов на соединение,которая может формироваться к данному процессу серверной части.Каждый клиентский процесс создает свой сокет. Заметим, что на стороне клиентасвязывать сокет необязательно (поскольку сервер может работать с любым клиентом, в частности,и с «анонимным» клиентом). Затем клиентский процесс может передать серверному процессусообщение, что он с ним хочет соединиться, т.е. передать запрос на соединение (посредствомсистемного вызова connect()). В данном случае возможны три альтернативы.Во-первых, может оказаться, что клиент обращается к connect() до того, как сервер перешелв режим прослушивания.
В этом случае клиент получает отказ с уведомлением, что сервер вданный момент не прослушивает сокет.Во-вторых, клиент может обратиться к connect(), а у серверного процесса в данный моментсформирована очередь необработанных запросов, и эта очередь пока не насыщена. В этом случаезапрос на соединение встанет в очередь, и клиентский процесс будет ожидать обслуживания.И, наконец, в-третьих, может оказаться, что указанная очередь переполнена. В этом случаеклиент получает отказ с соответствующим уведомлением, что сервер в данный момент занят.Итак, данная схема организована таким образом, что к одному серверному узлу можетиметь множество соединений.
Для организации этой модели имеется системный вызов accept(),который работает следующим образом. При обращении к данному системному вызову, если вочереди имеется необработанная заявка на соединение, создается новый локальный сокет,который связывается с клиентом. После этого клиент может посылать сообщения серверу черезэтот новый сокет. А сервер, в свою очередь, получая через данный сокет сообщения от клиента,«понимает», от какого клиента пришло это сообщение (т.е. клиент получает некотороевнутрисистемное именование, даже если он не делал связывание своего сокета).