О. С. Попова Хіхловська І. В. Системне та прикладне програмне забезпечення у телекомунікаціях Конспект

Вид материалаКонспект
Програмный интерфейс сокетов
Поддержка различных типов сокетов в доменах
Sock_stream ipproto_tcp(tcp)
Nix домен internet домен
Пример использования сокетов
Подобный материал:
1   ...   6   7   8   9   10   11   12   13   14

Програмный интерфейс сокетов


Сокет – это коммуникационный интерфейс взаимодействующих процессов, конкретный вид взаимодействия зависит от коммуникационного домена, в рамках которого создан сокет.

Название сокета тип сокета

Sock_dgram сокет датограмм

Sock_stream сокет потока

Sock_seqpacket сокет пакетов

Sock_rav сокет нужного уровня


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

Для создания сокета используются системный вызов socket(2);

#include

#include

int socket (int domain, int type, int protocol);

domain - домен

type – тип сокета

protocol – используемуй протокол

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

AF_UNIX—Домен локального межпроцессного взаимодействия в пределах единой системы процессов

AF_INET—Домен взаимодействия систем. протокол TCP/IP

AF_NS—Протоколы XEROX NS

Домен и семейство протоколов определяет адресное пространство взаимодействия, потому в названиях присутствует AF—AddressFamily—ceмейство адрасов. Допустимо также PF_UNIX,PF_INET—семейство протоколов.


Поддержка различных типов сокетов в доменах

домен

AF_UNIX

AF_INET

Тип сокета







SOCK_STREAM

Да

Да

SOCK_DGRAM

Да

Да

SOCK_SEQPACKER

Нет

Нет

SOCK_RAW

Нет

Нет


Для домена AF_INET возможны комбинации

Сокет Протокол

SOCK_STREAM IPPROTO_TCP(TCP)

SOCK_DGRAM IPPROTO_UDR(UDR)

SOCK_RAW IPPROTO_ICMP(ICMP)

SOCK_RAW IPPROTO_RAW(IP)


Для создания сокета его надо позиционировать в пространстве имен данного коммуникационного домена. Каждый коммуникационный канал определяется двумя узлами – источником и получателем данных и характеризуется пятью параметрам:
  1. Коммуникационным протоколом
  2. Локальным адресом
  3. Локальным
  4. Удаленным адресом
  5. Удаленным процессом


Адрес определяет ОС (или хост сети), а процесс – конкретное приложение, получающее или передающее данные. Конкретные значения и формат этих параметров определяются коммуникационным доменом.

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




Рис. Взаимодействие между процессами при создании виртуального канала с предварительным установлением




Рис. Взаимодействие между процессами, основанное на датограммах (без предварительного установления соединения).


На обоих рисунках показано, что фактической передаче данных предшествует начальная фаза связанная (binding) сокета, когда устанавливается дополнительная информация, необходимая для определения коммуникационного узла. Это реализуется с помощью системного вызова bind(2)

#include

#include

int bind (int sockfd,struct sockaddr *localaddr,int addrlen);

Здесь socket – дескриптор сокета, полученный при его создании ;аргумент *localaddr определяет локальный адрес, с которым необходимо связать сокет; параметр addrlen определяет размер адреса. Связывание с локальным адресом, который определяет два параметра коммуникационного канала (коммуникационный узел): локальный адрес и локальный процесс.

Адрес сокета зависит от коммуникационного домена, в рамках которого он определен, в файле struct sockaddr {

U_short;

Char sa_data [14];

};

Поле sa_family определяет коммуникационный домен (семейство протоколов), а sa_data – содержит собственно адрес, формат которого определен для каждого домена. Для внутреннего домена UNIX адрес выглядит так: определен в ;

Struct sockaddr_un {

Short sun_family; /*= = AF_UNIX*/

Char sun_path [108];

};

В качестве адреса в домене на одном ПК под управлением одной ОС коммуникационный узел может быть однозначно определен одним параметром –локальным процессом. В качестве адреса в домене UNIX используются имена файлов.

Для сетевого обмена данными необходимо указание локального процесса и хоста, на котором выполняется этот процесс. Для семейства протоколов TCP/IP определен формат адреса в файле ;

Struct sockakddr_in {

Short sin_family; /*= = AF_INET*/

U_short sin port;

Struct in addr sin_addr;

Char sin_zero[8];

};

Это IP-адрес хоста—32-разрядного число целое sin_addr, а процесс (приложение) адресуется 16-разрядным номером порта sin_port.




U NIX ДОМЕН INTERNET ДОМЕН


Адреса сокетов

Связывание необходимо для присвоения сокету ложного адреса и определения коммуникационного узла. В трех случаях для этой цели используется функция bind (2):
  1. Сервер регистрирует свой адрес. Этот адрес должен быть заранее известен клиентом, желающим общаться с сервером. Связывание необходимо прежде чем сервер будет готов к приему запросов от клиентов.
  2. При взаимодействии без предварительного установления связи и создания виртуального канала клиент также должен предварительно зарегистрировать свой адрес, который должен быть уникальным внутри коммуникационного домена. В случае домена UNIX об этом заботится само приложение этот адрес не должен быть заранее известен серверу, т.к. запрос всегда инициирует клиент, всегда автоматически передавая вместе с ним свой адрес. Полученный адрес удаленного узла затем используется сервером для мультиплексирования сообщений, передаваемых различным клиентам.
  3. Даже в случае взаимодействия с использованием виртуального канала клиент может при желании зарегистрировать свой адрес, не полагаясь при этом на систему


Назначение адреса для клиента можно выполнить также с помощью системного вызова connect(2), устанавливающего сокет клиента с локальным коммуникационным узлом. Вызов connect(2) имеет вид:

#include

# include

int connect (int.socket, struct sockaddr *servaddr,int addrlen);

вызов предполагает создание виртуального канала и используется для предварительного установления связи между коммуникационными узлами. В этом случае клиенту нет необходимости явно связывать сокет с помощью системного вызова bind (2). Локальный узел коммуникационного канала указывает дескриптором сокета socket, для которого система автоматически выбирает приемлемые значения локального адреса и процесса. Удаленный узел определяется аргументом servadder, который указывает на адрес сервера, addrlen задает его длину.

Вызов connect(2) может также применяться и клиентами. Использующими сокеты датограмм без создания виртуального канала. В этом случае connect(2) не вызывает фактического соединения с сервером, а просто сохраняет стандартные параметры (сервера, которому будут направляться датограммы). Клиент при этом не должен указывать адрес сервера при каждом отправлении данных.

Системный вызов listen(2) информирует систему, что сервер готов принимать запросы. Он имеет следующий вид:

#include

#include

int listen (int sockfd, int backlog);

Параметр sockfd определяет сокет, который будет использоваться для получения запросов в предположении, что сокет был предварительно связан с известным адресом, т.е. предварительно создал виртуальный канал между сервером и каналом. Параметр backlog указывает максимальное число запросов на установление связи, которые могут ожидать обработки сервером.

Фактическую обработку запроса клиента на установление связи производит системный вызов assept (2)

#include

#include

int assept (int sockfd,struct sockaddr *clntaddr, int *addrlen);

вызов assept (2)извлекает первый запрос из очереди и создает новый сокет, характеристики которого не отличаются от сокета sockfd, и таким образом завершает создание виртуального канала со стороны сервера. Одновременно assept (2) возвращает параметры удаленного коммуникационного узла – адрес клиента elntaddr и его размер addrlen. Новый сокет используется для обслуживания созданного виртуального канала, а полученный адрес клиента не помогает анонимность клиента. Типичный сценарий.

Sockfd = Socket (…) создать сокет

Bind (sockfd,…); связать его с известным локальным адресом

Listen (sockfd,…); организовать очередь запросов

For ( ; ;) {

Newsockfd – accept (sockfd,…); померить запрос

If (fork () = = 0 { породить дочерний процесс

Close (sockfd); дочерний процесс



exit (0);

}

else

close (newsockfd); родительский процесс

}

В это время как дочерний процесс обеспечивает фактически обмен данными с клиентом, родительский процесс продолжает “прослушивать” запросы клиентов, порождая для них отдельные процессы – обработки, очередь позволяет буферезировать запросы на время, пока сервер завершает вызов assept (2) и затем создает дочерний процесс. Новый соке newsockfd, полученный в результате вызова assept(2), адресует полностью определенный коммуникационный канал: протоколы и полные адреса обоих узлов – клиента и сервера. Для сокета sockfd определена только локальная часть канала. Это позволяет серверу продолжать использовать sockfd для “прослушивания последующих запросов”.

Если для сокетов потока на приема и передаче данных могут быть использованы стандартные вызовы read (2) и write (2), то сокеты датограмм должны пользоваться специальными системными функциями: send (2),которые используются для передачи данных удаленному узлу, а фунуции и recvfrom (2) – для их приема. Отличие между ними заключается в том, что send(2) и recv(2) используются только для подсоединения сокета после вызова connect (2). Все эти вызовы используются в качестве первого аргумента дескриптор сокета, через который производится обмен данными. Аргумент msg содержит сообщение длиной len,которое должно быть передано по адресу Toaddr, длина которого составляет Tolen байтов. Для функции Gend(2) используется адрес получателя, установленный предшествующим вызовом Connect(2). Аргумент вид представляет собой буфер, в котором копируются полученные данные

Параметр flags может принимать следующее назначение:

MSG_OOB передать или принять экстренные данные (out-of-band) вместо обычных

MSG_PEEK просмотреть данные, не удаляя их из системного буфера

#include

#include

int send (int s,const char*msg, int len, int flags);

int send to (int s, const char*msg, int len, int flags, const struct sockaddr*toaddr, int tolen);

int recv (int s, shar*bul, int len, int flags);

int recvfrom (int s, char*bul, int len, int flags, struct sockaddr*fromaddr, int*fromlen);

Пример использования сокетов


Сокеты принадлежат домену UNIX, клиент посылает серверу сообщение “Hello”, а сервер отправляет его обратно клиенту, который после получения выводит сообщение на экран. Т.к. обмен данными происходит внутри одной ОС UNIX, сокеты датаграмм, которые здесь используются, не отличаются от сокетов потока. В качестве адреса сервера предлагается имя файла ./echo.server, считается что он один из данного каталога запущен. Предполагается, что клиенты знают этот адрес. Сервер связывает созданный сокет с этим локальным адресом и таким образом регистрируется в системе, после чего он готов к получению и обработке сообщений. Сервер начинает бесконечный цикл, ожидая сообщений от клиентов и блокируясь на вызове recvfrom(2). При получении сообщений сервер отправляет его обратно, вызывая sendto(2)

Сервер:

#include

#include

#include

#define MAXBUF 256

char buf [MAXBUF];

main()

{ struct sockaddr_un serv_addr, clnt_addr;

int sockfd;

int saddrlen, caddrlen, max_caddrlen, n;

/*создадим сокет*/

if ((sockfd = coscket(AF)UNIX, SOCK_DGRAM, 0))<0 {

printf(«невозможно создать сокет/n»); exit (1);

/* свяжем сокет с известным локальным адресом. Т.к. адрес в домене UNIX представляет собой имя файла, который будет создан системным вызовом bind(2), сначала следует удалить файл с этим именем в случае, если он сохранился от предыдущего запуска сервера */

unlink (“,/echo.server”);

bzero (s serv_addr, sizeof (serv_addr));

serv_addr.sun_family = AF_UNIX;

strcpy (serv _addr. Sun_path, “./echo.server”);

saddrlen = sizeof (serv_addr. Sun_family) + strlen(serv_addr. Sun_path);

if (bind (sockfd, (struct sockaddr *) f serv_addr, saddrlen)<0)

{printf(«ошибка связывания сокета с адресом./n»);

exit(1);

}

/*запускается бесконечный цикл чтения сообщений от клиентов и отправления их обратно*/

max_caddrlen=sizeof(clnt_addr);

for( ; ;) {

caddrlen=max_caddrlen;

n=recvfrom(socket, buf, MAXBUF, 0, (struct sockaddr*) & clnt_addr, & caddrlen);

if (n<0) {“ошибка приема./n”); exit(1);}

/* благодаря вызову recvfrom(2), становится известным адрес клиента, от которого получено сообщений. Этот адрес используется для передачи сообщения одратно отправителю*/

if (sendto(sockfd, buf, n,0 (struct sockaddr*) & clnt_addr, caddrlen) :=n){

print (”ошибка передичи/n”); exit(1);}

}

}

Клиент создает сокет датаграмм и связывает его со своим уникальным адресом, который определяется уникальностью имени файла (одновременно могут работать несколько клиентов). Для обеспечения уникальности используется mktemp(3e), позволяющая по заданному шаблону /tmp/clnt.XXXX и на основании идентификатора текущего процесса получить уникальное имя, заменяя символы «ХХХХ». Связывание сокета позволяет при отправлении сообщения неявно указать его «адрес отправления», так что сервер отправляет его обратно.

Клиент

#include

#include

#include

#include NULL 0

char *msg=”Hello/n”;

#define MAXBUF 256

char buf [MAXBUF];

main ()

{

struct sockaddr_un serv_addr, clnt_addr;

int sockfd;

int saddrlen, caddrlen, msglen, n;

/*Установим адрес сервера, с которым мы будем обмениваться данными. Для этого заполняется структура данных sockaddr_un, которая используется при отправлении данных серверу с помощью вызова sendto(). Значение адреса известно по предварительной договоренности */.

Bzero (fserv)addr, sizeof(serv_addr));

Serv_adr.sun_family=AF_UNIX;

Strcpy(serv_addr.sun_paht, “./echo.server”);

Saddrlen=sizeof(serv_addr.sun_family)+strlen(sorv_addr.sun_path);

/*создадим сокет датаграмм*/

if ((socket=socket(AF_UNIX,sock_dgram,0))<0) {

printf(“невозиожно создать сокет.n”); exit(1);}

/*Необходимо связать socket с некоторыми локальным адресом, чтобы сервер мог возвратить посланное сообщение. Этот адрес должен быть уникальным в пределах коммуникационного домена – данной ОС. Для обеспечения этого условия используется функция mkstemp(3c), которая возвращает уникальное имя, основанное на представленном шаблоне и идентификаторе процесса PID*/

bzero (& clnt_addr, sizeof(clnt_addr));

clnt_addr.sun_family = AF_UNIX;

strepy(cl_addr.sun_path,”/tmp/clnt.XXXX”);

mkstmp(clnt_addr.sun_path);

caddrlen=sizeof(clnt_addr.sun_family)+atrlen(clnt_addr.sun_path);

if (bind sochfd, (struct sockaddr*)& clnt_addr,c addrlen)<0)

{

printf(‘ошибка связывания 0сокета.n’); exit(1);

}

/*отправление текста “Hello”

msglen = strlen (msg);

if (sendto(sockfd, msg, msglen,0,(struct sockaddr *)&serv_addr, saddrlen)!=msglen)

print (“ошибка передачи сообщения /n”); exit(1);

}

/*Чтение эха*/

if ((n=recvfrom(sockfd, buf, MAXBUF,0,NULL,0))<0)

printf (“Ошибка получения сообщения/n”);

exit(1);

/*и вывод его на экран */

ptint (“эхо%; s/n”,buf);

/*Уничтожение сокета*/

close (sockfd);

unlink (clnt_addr.sun_path);

exit(0);

}


Сравнение различных систем межпроцесорного взаимодействия




Каналы

FIFO

Сообщения

Разделяемая память

Сокеты домен UNIX

Пространство имен

__________




Ключ

Ключ

Имя файла

Объект

Системный канал

Именование канала

Очередь сообщений

Разделяемая память

Коммуникационный узел

Создание объекта

Pipe()

Mknod()

Msgget()

Shmdet()

Socket()

Связывание

Pipe()

Open

Msgget()

Shmat()

Bind()

Connect()

Передача данных

Read()

Write()

Read()

Write()

Msgrcv()

Msgshd()

Непосредственный доступ

Read()

Write()

Recv()

Send()

Recvfrom()

Sendtu()

Уничтожение

Close(0)

Close(0)

Unlink()

Msgcte()

Shmdt()

Close()

unlink