Помощь в учёбе, очень быстро...
Работаем вместе до победы

Интерфейс сетевой подсистемы

РефератПомощь в написанииУзнать стоимостьмоей работы

Структура sockaddr in предназначена для работы с протоколами TCP/ IP. Значение поля sin_family всегда равно AF INET. Поле sinjport содержит номер порта, который намерен занять процесс. Если значение этого поля равно нулю, то ОС сама выделит свободный номер порта для сокета. Поле sin addr содержит IP-адрес, к которому будет привязан сокет. Структура in addr содержит ноле s addr, в которое обычно… Читать ещё >

Интерфейс сетевой подсистемы (реферат, курсовая, диплом, контрольная)

При работе с сокетами используются несколько вспомогательных структур, подробно описанных в работе Г. Дейтела[1]. Во-первых, для хранения информации о сокете существует стандартная структура, определенная в :

struct sockaddr

{.

и char sa_len; /* total length */ sa _family_t sa _family /* address family V char sa_data[]; /* actually longer, address value */

};

Во-вторых, имеется структура для работы сокетов с протоколом IP определенная следующим образом:

struct sockaddrjin

{.

sa J’amilyt sin _family,

in _port_t sin _port-,

struct inaddr sin addr, unsigned char sin_zero[8J-,

};

Здесь структура in addr определена как:

struct in addr

{.

in addr t saddr,

};

Структура sockaddr in предназначена для работы с протоколами TCP/ IP. Значение поля sin_family всегда равно AF INET. Поле sinjport содержит номер порта, который намерен занять процесс. Если значение этого поля равно нулю, то ОС сама выделит свободный номер порта для сокета. Поле sin addr содержит IP-адрес, к которому будет привязан сокет. Структура in addr содержит ноле s addr, в которое обычно записывается 32-битное значение IP-адреса. Для перевода адреса в целое число из строкового представления используют функцию inet addr, которой в качестве аргумента передается указатель на строку, содержащую IP-адрес в виде четырех десятичных чисел, разделенных точками. Кроме того, имеются следующие константы, играющие особую роль:

INADDR_ANY — все адреса локального хоста (0.0.0.0);

INADDRLOOPBACK — адрес loopback — (127.0.0.1);

INADDR BROADCAST — широковещ. адр. (255.255.255.255).

Существует несколько системных функций работы с сокетами. Функция socketQ создает сокет и, следовательно, устанавливает оконечную точку линии связи:

sd = socket (format, type, protocol);

где поле format обозначает домен, т. е. способ, каким будет устанавливаться связь; type — тин связи через сокет — потокориентированный (SOCK_ STREAM) или дейтаграммный (SOCKDGRAM); protocol — тип протокола, управляющего взаимодействием. Обычно этот параметр задается равным нулю, при этом используется протокол, принятый по умолчанию для данного типа сокетов (TCP для сокетов типа SOCK_STREAM и UDP для сокетов типа SOCK DGRAM). Дескриптор сокета sd, возвращаемый функцией socket, используется другими системными функциями. В случае возникновения ошибки функция возвращает значение -1. Закрытие сокетов выполняет функция closeQ.

Функция socketQ создает некий абстрактный сокет. Для его привязки с адресом компьютера и номером порта используется функция bindQ:

bind (int sd, const struct sockaddr *address, int length);

где sd — дескриптор сокета; address — адрес структуры, определяющей идентификатор, характерный для данной комбинации домена и протокола (в функции socket). В случае использования протокола IP это структура тина sockaddr in, где length — длина структуры address, а без этого параметра ядро не знало бы, какова длина структуры, поскольку для разных доменов и протоколов она может быть различной. Например, для домена «UNIX» (параметр равен AF_UNIX) структура содержит имя файла. Процессы-серверы связывают сокеты с именами и объявляют о состоявшемся присвоении имен процессам-клиентам.

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

connected, address, length);

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

Если сокет — дейтаграммный, то сообщаемый функцией connectQ ядру адрес будет использоваться в последующих обращениях к функции sendQ через данный сокет. В момент вызова никаких соединений не производится.

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

listen (int sd, int qlength);

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

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

nsd = accepted у address, addrlen);

где sd определяет дескриптор сокета, который принимает запросы на установление соединений; address — указатель на пользовательский массив, в котором ядро возвращает адрес подключаемого клиента, либо NULL, либо указатель на структуру, в которую будет помещен адрес удаленного сокета после возврата из функции; addrlen — указатель на переменную, в которой хранится длина структуры address.

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

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

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

Схема действий пассивного процесса выглядит следующим образом:

socket () // Создание сокета.

bind () // Привязка сокета к номеру порта.

listen () // Создание очереди соединений.

while (){

accept () // Прием запроса на установление соединения … // Обмен данными.

close () // Закрытие соединения.

}

Для активной стороны схема действий выглядит так:

socket () //Создание сокета.

connect () // Установление соединения.

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

close () //Закрытие соединения После создания сокета пассивная сторона сразу устанавливает соединение. Функции send () и recv () выполняют передачу данных через подключенный сокет. Синтаксис вызова функции send ():

count = send (sdymsgflengthjlags);

где sd — дескриптор сокета; msg — указатель на посылаемые данные; length — размер данных; count — количество фактически переданных байт. Значение flags является результатом логического ИЛИ нуля или некоторого числа следующих констант:

MSGOOB — передать срочные данные.

MSGDONTROUTE — игнорировать параметры маршрутизации.

SOFOOB (out-of-band) — вставка служебной информации в данные.

Программа удаленной регистрации, например, может послать out-ofband сообщение, имитирующее нажатие на клавиатуре терминала клавиши «delete». Если на пути следования пакета стоит несколько маршрутизаторов и они работают с ограниченной длиной пакета, то длинный пакет должен разбиваться на несколько, каждый со своей заголовочной частью. Может возникнуть ситуация, когда:

  • • разные части пакета попадут к пользователю в другом порядке;
  • • данные от нескольких пакетов могут попасть к пользователю не в том порядке, в котором они отправлены.

В этих случаях флаг SOF_OOB обеспечивает очередность рассмотрения пакетов и восстановление нужного порядка следования передаваемой информации.

В случае успешного завершения sendQ возвращает число переданных байт. В противном случае возвращаемое значение равно -1.

Синтаксис вызова системной функции recv ().

count = recv (sd> bufl lengthy flags);

где buf- массив для приема данных; length — ожидаемый объем данных; count — количество байт, фактически переданных пользовательской программе. Значение flags является результатом логического ИЛИ нуля или некоторого числа следующих констант:

MSGPEEK — данные не удаляются из буфера приема. Следующий вызов функции reev прочитает те же данные;

MSG OOB — принять срочные данные;

MSG_WAITALL — блокировать функцию, пока не будет принят полный объем данных, определенный аргументом length (карантинная услуга). Функция может вернуть меньший объем данных в случае обрыва соединения, ошибки, связанной с сокетом, использования флага MSG PEEK.

В случае успешного завершения функция возвращает число принятых байт. В противном случае возвращается -1.

Флаги (flags) могут быть установлены таким образом, что поступившее сообщение после чтения и анализа его содержимого не будет удалено из очереди, или настроены на получение данных out-of-band.

В дейтаграммных версиях обмена информацией используются функции sendtoQ и recvfromQ. Функция sendtoQ, предназначенная для отправки данных, имеет вид:

sendto (int sd, const void *mess, size_t length, intflags,

const struct sockaddr *dest_addr, socklen t dest len);

Аргументы функции имеют следующие значения: sd — сокет, через который будут отправлены данные; mess — указатель на буфер, содержащий данные для отправки; length определяет длину сообщения в байтах; flags — определяет параметры передачи сообщения. Значение flags является результатом логического ИЛИ нуля или некоторого числа следующих констант:

  • • MSG_OOB — передать срочные данные (не поддерживается протоколом UDP);
  • • MSG_DONTROUTE — игнорировать параметры маршрутизации.

Параметр destaddr указывает на структуру, содержащую адрес получателя; dest len — определяет длину структуры, на которую указывает destaddr.

Функция sendto () возвращает число переданных байт в случае успешного завершения и -1 — в противном случае. Следует заметить, что успешное выполнение функции sendtoQ не гарантирует доставку данных получателю.

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

ssizejt recvfrom (int sd, void *buff,

size fl length, int flags, struct sockaddr *addr, socklen t *addr_len);

Аргументы функции имеют следующее значение: sd — сокет, из которого производится чтение данных; huff- указатель на выходной буфер; length — определяет длину буфера, на который указывает аргумент buff, addr указатель на структуру, в которую будет помещен адрес отправителя; addrflen — длина структуры, на которую указывает address; flags — определяет параметры приема данных. Значение flags является результатом логического ИЛИ нуля или некоторого числа следующих констант:

  • • MSG_PEEK — оставить принятые данные в буфере приема. Следующий вызов recvfromQ;
  • • MSG_OOB — принимать только срочные данные (не поддерживается протоколом UDP);
  • • MSG WAITALL — блокировать функцию, пока не будет принят полный объем данных, определенный аргументом length (карантинная услуга). Функция может вернуть меньший объем данных в случаях: обрыва соединения; ошибки, связанной с сокетом; использования флага MSG_PEEK.

Функция recvfromQ возвращает количество данных, записанных в буфер. Если при ее выполнении возникли ошибки, то возвращается значение -1. Для протокола UDP данные, пришедшие в одном пакете, должны быть прочитаны одним вызовом функции recvfromQ. Если длина буфера недостаточна для размещения всех данных, то лишние байты отбрасываются.

После выполнения подключения к сокетам потокового типа процессы могут вместо функций send () и recv () использовать функции read () и write (). Таким образом, после согласования типа протокола серверы могут порождать процессы, работающие только с функциями readQ и writeQ, словно с обычными файлами!

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

shutdown (sd, mode)',

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

Для получения информации о хостах введена специальная структура hostent, имеющая вид:

struct hostent {

char *h_ name; /* Официальное имя хоста */

char **h_aliases; /* Массив псевдонимов хоста */.

int haddrtype; /* Тип адреса (обычно AF INET) */.

int hlength; /* Длина адреса в байтах */.

char **h_addr_list; /* Список адресов хоста */.

}.

Для получения адреса хоста по его имени используется функция gethostbynamef);

struct hostent *gethostbyname (const char *name);

Для определения имени хоста по его адресу применяется функция gethostbyaddrf). В качестве аргументов функции передаются указатель на адрес хоста, длина адреса и его тип (AF INET):

struct hostent *gethosthyaddr (const void «addr, sizetlen, int type);

В случае возникновения ошибок обе функции возвращают NULL. Код ошибки помещается в переменную h ermo.

Ниже приведены примеры кодов на языке Си для создания приложений для серверной и клиентской сторон:

Г.

/* The client part of a client-server pair. This simply takes /* two numbers and adds them together, returning the result /* to the client

V

#include

#include

#include

#include

#include

#define PORT9000 /* Arbitrary non-reservedport */

# define HOST «nexus.iu.hio.no»

#define bufsize 20

main (int arge, char *argv[J)

{

struct sockaddr in ein; struct hostent *hp; char buffer[bufsize]; int sd;

if (arge ≠ 4)

{

printf («syntax: client a + b «); exit (1);

}

if ((ftp = gethostbyname (HOST)) == NULL)

{

perror («gethostbyname: «); exit (1);

}

memset (&cin, 0, sizeof (cin)); /*zero memory */ cin. sinj'amily = AFINET; cin.sinaddr.saddr =.

((struct in addr *)(hp->h_addr))->s_addr; cin. sin_port = htons (PORT);

printf («Trying to connect to %s = %s «, HOST, inetntoa (cin.sinaddr));

if ((sd = socket (AF_INET, SOCK_STREAM, 0))—-1)

{

perror («socket»); exit (1);

}

if (connected,&cin, sizeof (cin)) == -1)

{

perror (aconnect"); exit (1);

}

sprintf (buffer,"%s + %s", argv[1], argv[3]);

if (send (sd, buffer, strlen (buffer), 0) == -1)

{

perror («send»); exit (1);

}

if (recv (sd, buffer, bufsize, 0) == -1)

{

perror («recv»); exit (1);

}

printf («Server responded with %s «, buffer);

close (sd); unlink («./socket» );

}.

y'****************************************** tf.*******^.

r

/* The server part of a client-server pair. This simply takes /* two numbers and adds them together, returning the result /* to the client

V.

ttinclude ttinclude ttinclude ttinclude ttinclude

ttdefine PORT9000 ttdefine bufsize 20 tt define queuesize 5 ttdefine true 1 ttdefine false 0

main ().

{.

struct sockaddrin cin; struct sockaddr in sin; struct hostent *hp; char bufferfbufsize]; int sd, sd client, addrlen;

memset (&sin, 0, sizeof (sin)); /*zero memory V sin.sinjamily = AFINET;

sin.sin addr. s addr = INADDR ANY; /* Broadcast address */ sin. sin _port = htons (PORT);

if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)

{.

perror («socket»); exit (1);

}.

if (bind (sd,&sin, sizeof (sin)) == -/).

{.

perror («bind»); exit (1);

}.

if (listened, queuesize) == -1)

{.

perror («listen»); exit) 1);

}.

while (true)

{.

if ((sdclient = accept (sd,&cin,&addrlen)) == -1)

{.

perror («accept»); exit (1);

}

if (recv (sd client, buffer, sizeof{buffer), 0) == -1)

{

perror («recv»); exit (1);

}

if (IDoService (buffer)) break-,

if (send (sdclient, buffer, strlen (buffer), 0) == -1)

{

perror («send»)-, exit (1);

}.

close (sdclient);

}

close (sd);

printf («Server closing down… «);

}.

***********************************************^/.

/*.

/* This is the protocol section. Here we must check /* that the incoming data are sensible

V

DoService (char *buffer).

{.

int a=0, b=0;

printfi«Received: %s «, buffer); sscanf (buffer,"%d + %d » ,&a,&b); if (a >0&& b>0)

{

sprintf (buffer," %d + %d = %d", a, b, a+b); return true;

}

else

{

if (stmcmp (‘‘ halt", buffer, 4) == 0)

{

sprintf (buffer," Server closing downV'); return false;

}

else

{

sprintf (buffer," Invalid protocol"); return true;

}

}

}

Рассмотрим работу программы, представленной выше. Процесс создает в домене AF_INET сокет потокового типа и присваивает ему идентификатор sd. Затем с помощью функции listenQ устанавливается длина очереди поступающих сообщений (queuesize=5) и начинается цикл ожидания поступления запросов.

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

Итак, для организации серверного процесса:

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

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

socket (AF_INET, SOCK_STREAM, 0);

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

Если серверу нужно одновременно следить за состоянием сети и выполнением локальных процессов, он использует два сокета и с помощью функции select{) определяет, с каким клиентом устанавливается связь в данный момент.

Итак, для создания клиентского процесса необходимо:

  • • создание сокета с помощью функции socket{)
  • • установление связи с помощью функции connect(), и при ее успешной реализации выполнение операций чтения — записи через созданный сокет.

Механизм сокетов представляет собой единственный механизм передачи информации между ОС в сети. Другого механизма не существует!

  • [1] Дейтел Г.

    Введение

    в операционные системы. М.: Мир, 1987.

Показать весь текст
Заполнить форму текущей работой