logo
Лекции / Novel, Unix, WindowsNT

Сокеты.

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

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

В целях создания более универсальных методов взаимодействия процессов на основе использования многоуровневых сетевых протоколов для системы BSD был разработан механизм, получивший название “sockets”. В данном разделе рассматриваются некоторые аспекты применениясокетов(на пользовательском уровне представления).

Структура ядра имеет три уровня: сокет, протоколов и устройств см Рисунок ???. Уровень сокетов выполняет функции интерфейса между обращениями к операционной системе (системным функциям) и средствами низких уровней, уровень протоколов содержит модули, обеспечивающие взаимодействие процессов на рисунке упомянуты протоколы TCP и IP, а уровень устройств содержит драйверы, управляющие сетевыми устройствами. Допустимые сочетания протоколов и драйверов указываются при построении системы (в секции конфигурации); этот способ уступает по гибкости вышеупомянутому потоковому механизму. Процессы взаимодействуют между собой по схеме клиент-сервер: сервер ждет сигнала от сокета, находясь на одном конце дуплексной линии связи, а процессы-клиенты взаимодействуют с сервером через сокеты, находящееся на другом конце, который может располагаться на другой машине. Ядро обеспечивает внутреннюю связь и передает данные от клиента к серверу.

Сокеты, обладающие одинаковыми свойствами, например, опирающиеся на общие соглашения по идентификации и форматы адресов (в протоколах), группируются в домены (управляемые одним узлом). В системе BSD 4.2 поддерживаются домены: “UNIX system” - для взаимодействия процессов внутри одной машины иInternet- для взаимодействия через сеть с помощью протокола DARPA (Управление перспективных исследований и разработок Министерства обороны США). Сокеты бывают двух типов: потокориентированнный сокет (виртуальный канал, если пользоваться терминологией Беркли) и поток не ориентированный сокет - дейтаграмма.

Виртуальный канал обеспечивает надежную доставку данных с сохранением исходной последовательности. Дейтаграммы не гарантируют надежную доставку с сохранением уникальности и последовательности, но они более экономны в смысле использования ресурсов, поскольку для них не требуются сложные установочные операции; таким образом, дейтаграммы полезны в отдельных случаях взаимодействия.

Для каждой допустимой комбинации типа домен - сокет в системе поддерживается умолчание на используемый протокол. Так, например, для домена “Internet” услуги виртуального канала выполняет протокол транспортной связи TCP, а функции дейтаграммы - пользовательский дейтаграммный протокол UDP.

Существует несколько системных функций работы с сокетами. Функция socket

устанавливает оконечную точку линии связи.

sd = socket(format,type,protocol);

Formatобозначает домен (“UNIXsystem” или “Internet”),туре- тип связи через сокет (виртуальный канал или дейтаграмма), аprotocol- тип протокола, управляющего взаимодействием. Дескриптор сокетаsd, возвращаемый функциейsocket, используется другими системными функциями. Закрытие сокет выполняет функцияclose.

Функция bindсвязывает дескриптор сокета с именем:

bind(sd,address,length);

где sd - дескриптор сокета, address- адрес структуры, определяющей идентификатор, характерный для данной комбинации домена и протокола (в функцииsocket).Length- длина структурыaddress; без этого параметра ядро не знало бы, какова длина структуры, поскольку для разных доменов и протоколов она может быть различной. Например, для домена “UNIXsystem” структура содержит имя файла. Процессы-серверы связывают сокета с именами и объявляют о состоявшемся присвоении имен процессам-клиентам.

С помощью системной функции connectделается запрос на подключение к существующему сокету:

connect(sd,address,length);

Семантический смысл параметров функции остается прежним (см. функцию bind), ноaddressуказывает уже на выходной сокет, образующий противоположный конец линии связи. Оба сокета должны использовать одни и те же домен и протокол связи, и тогда ядро удостоверит правильность установки линии связи.

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

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

listen(sd,qlength)

где sd - дескриптор сокета, а qlength- максимально-допустимое число запросов, ожидающих обработки.

Системная функция acceptпринимает запросы на подключение, поступающие на вход процесса-сервера:

nsd = accept(sd,address,addrlen);

где sd - дескриптор сокета,address- указатель на пользовательский массив, в котором ядро возвращает адрес подключаемого клиента,addrlen- размер пользовательского массива. По завершении выполнения функции ядро записывает в переменнуюaddrlenразмер пространства, фактически занятого массивом. Функция возвращает новый дескриптор сокета (nsd), отличный от дескриптораsd.

Процесс-сервер может продолжать слежение за состоянием объявленного сокета, поддерживая связь с клиентом по отдельному каналу.

Функции sendиrecvвыполняют передачу данных через подключенный сокет.

Синтаксис вызова функции send:

count = send(sd,msg,length,flags);

где sd- дескриптор сокета, msg - указатель на посылаемые данные, length -размер данных, count - количество фактически переданных байт. Параметр flags может содержать значение SOF_OOB (out-of-band - вставка служебной информации в данные), если посылаемые данные не учитываются в общем информационном обмене между взаимодействующими процессами. Программа удаленной регистрации, например, может послать out-of-band сообщение, имитирующее нажатие на клавиатуре терминала клавиши “delete”. Синтаксис вызова системной функции recv:

count = recv(sd,buf,length,flags);

где buf - массив для приема данных, length - ожидаемый объем данных, count -количество байт, фактически переданных пользовательской программе. Флаги (flags) могут быть установлены таким образом, что поступившее сообщение после чтения и анализа его содержимого не будет удалено из очереди, или настроены на получение данных out-of-band. В дейтаграммных версиях указанных функций, sendto и recvfrom, в качестве дополнительных параметров указываются адреса. После выполнения подключения к сокетам потокового типа процессы могут вместо функций send и recv использовать функции read и write. Таким образом, согласовав тип протокола, серверы могли бы порождать процессы, работающие только с функциями read и write, словно имеют дело с обычными файлами.

Функция shutdown закрывает сокетовую связь:

shutdown(sd,mode);

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

Системная функция getsockname получает имя сокетовой связи, установленной ранее с помощью функции bind:

getsockname(sd,name,length);

Функции getsockopt и setsockopt получают и устанавливают значения различных связанных с сокетом параметров в соответствии с типом домена и протокола.

Рассмотрим обслуживающую программу, представленную на Рисунке.

Процесс создает в домене “UNIX system” сокет потокового типа и присваивает ему имя sockname. Затем с помощью функции listen устанавливается длина очереди поступающих сообщений и начинается цикл ожидания поступления запросов.

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

Ниже показан пример процесса-клиента, ведущего общение с сервером.

#include <sys/types.h>

#include <sys/socket.h>

main()

{

int sd, ns ;

char buf[256];

struct sockaddr sockaddr;

int fromlen;

sd = socket(AF_UNIX,SOCK_STREAM,0);

/* имя сокета - не может включать пустой символ */

bind(sd, ”sockname”, sizeof (“sockname”) - 1);

listen(sd,1);

for (;;)

{

ns = accept(sd, &sockaddr, &fromlen);

if (fork() == 0)

{

/* потомок */

close(sd);

read(ns, buf, sizeof(buf));

printf (“сервер читает ‘%s’\n” buf);

exit();

}

close(ns);

}

}

Рисунок ???. Процесс-сервер в домене “UNIX system”

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

#include <sys/types.h>

#include <sys/socket.h>

main()

{

int sd, ns;

char buf[256];

struct sockaddr sockaddr;

int fromlen;

sd = socket(AF_UNIX,SOCK_STREAM,0);

// имя в запросе на подключение не может включать

// пустой символ

if (connect(sd, ”sockname”, sizeof(“sockname”) - 1) == -1)

exit();

write(sd, ”higuy”,6);

}

Рисунок. Процесс-клиент в домене “UNIX system”.

Если сервер обслуживает процессы в сети, указание о том, что сокет принадлежит домену “Internet”, можно сделать следующим образом:

socket(AF_INET,SOCK_STREAM,0);

и связаться с сетевым адресом, полученным от сервера. В системе BSD имеются библиотечные функции, выполняющие эти действия. Второй параметр вызываемой клиентом функции connect содержит адресную информацию, необходимую для идентификации машины в сети (или адреса маршрутов посылки сообщений через промежуточные машины), а также дополнительную информацию, идентифицирующую приемный сокет машины-адресата. Если серверу нужно одновременно следить за состоянием сети и выполнением локальных процессов, он использует два сокета и с помощью функции selectопределяет, с каким клиентом устанавливается связь в данный момент.