Сетевые средства: основные понятия и объекты, ассоциированные с сетевыми средствами

Сеть как совокупность взаимосвязанных хостов. Аналог SOCK_DGRAM с дополнительной возможностью доступа к протокольным заголовкам и другой информации нижнего уровня. Функции для опроса данных о сети и для работы с сокетами. Мультиплексирующая программа.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 27.11.2013
Размер файла 58,6 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Занятие №1

Сетевые средства: основные понятия и объекты, ассоциированные с сетевыми средствами. Функции для опроса данных о сети и для работы с сокетами

  • Содержание занятия
  • 1. Теоретическая часть. Сетевые средства
  • 1.1 Основные понятия и объекты
  • 1.2 Опрос данных о сети
  • 1.3 Функции для работы с сокетами
  • 1. 4Примеры программ работы с сокетами
  • 1. Теоретическая часть. Сетевые средства.

1.1 Основные понятия и объекты

сеть хоста мультиплексирующий программа

Стандарт POSIX-2001 определяет сеть как совокупность взаимосвязанных хостов. Тем самым предполагается, что сетевая инфраструктура остается скрытой от приложений, которым предоставляются высокоуровневые средства взаимодействия в распределенной среде.

Под сетевым адресом понимается видимый в пределах сети идентификатор, используемый для обозначения оконечных точек сети. Адреса есть у определенных оконечных точек хостов, могут они быть и у хостов в целом.

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

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

Обычно оконечной точкой служит аппаратный сетевой интерфейс, посредством которого данные передаются и принимаются, однако с таким интерфейсом, как шлейфовый (loopback), никакой аппаратуры не ассоциировано.

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

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

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

Для преобразования значений типов uint16_t и uint32_t из хостового порядка байт в сетевой служат функции htons() и htonl(); функции ntohs() и ntohl() осуществляют обратную операцию.

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

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

Основные описания, относящиеся к сокетам, сосредоточены в заголовочном файле <sys/socket.h>. Фигурирует в нем и упомянутая выше структура sockaddr для адреса сокета, которая должна содержать по крайней мере следующие поля.

sa_family_t sa_family;

/* Адресное семейство */

char sa_data [];

/* Адрес сокета (данные переменной длины) */

Адресное семейство соответствует конкретной среде взаимодействия. Стандарт POSIX-2001 определяет три таких семейства.

AF_UNIX

Адресное семейство UNIX поддерживает межпроцессное взаимодействие в пределах одной системы. Формально это можно считать вырожденным случаем сетевого взаимодействия. Описания, специфичные для данного семейства, содержатся в заголовочном файле <sys/un.h>.

AF_INET

Адресное семейство, поддерживающее взаимодействие по протоколам IPv4. Специфичные для него описания располагаются в заголовочном файле <netinet/in.h>.

AF_INET6

Взаимодействие по протоколам IPv6 (необязательная возможность). За счет поддержки адресов IPv6, отображенных на IPv4, обеспечивается совместимость с приложениями, использующими IPv4. Применяемые эти адресным семейством описания распределены по заголовочным файлам <netinet/in.h>, <arpa/inet.h> и <netdb.h>.

В пределах каждого адресного семейства могут существовать сокеты нескольких типов. В стандарте POSIX-2001 их четыре.

SOCK_STREAM

Сокеты данного типа поддерживают надежные, упорядоченные, полнодуплексные потоки октетов в режиме с установлением соединения.

SOCK_SEQPACKET

Аналог SOCK_STREAM с дополнительным сохранением границ между записями.

SOCK_DGRAM

Передача данных в виде датаграмм в режиме без установления соединения.

SOCK_RAW

(Необязательная возможность). Аналог SOCK_DGRAM с дополнительной возможностью доступа к протокольным заголовкам и другой информации нижнего уровня. Создавать сокеты этого типа могут лишь процессы с соответствующими привилегиями.

Для каждого адресного семейства каждый тип сокета может поддерживаться одним или несколькими протоколами. В частности, в адресном семействе AF_INET для сокетов типа SOCK_STREAM подразумеваемым является протокол с именем IPPROTO_TCP, а для типа SOCK_DGRAM - IPPROTO_UDP; посредством "прозрачных" сокетов (SOCK_RAW) можно воспользоваться протоколом ICMP, задав имя IPPROTO_ICMP, и т.д.

Общая логика работы с сокетами состоит в следующем. Сокеты создаются с помощью функции socket(), которой в качестве аргументов передают адресное семейство, тип сокета и протокол, а в результате получают открытый файловый дескриптор. Затем посредством функции bind() сокету присваивают локальный адрес. Если сокет ориентирован на режим с установлением соединения, то, прибегнув к функции listen(), его следует пометить, как готового принимать соединения. Реальный прием соединений выполняет функция accept(), создающая для каждого из них новый сокет по образу и подобию "слушающего". В свою очередь, потенциальный партнер по взаимодействию инициирует соединение, применяя функцию connect(). (В режиме без установления соединения функция connect() позволяет специфицировать адрес отправляемых через сокет датаграмм.)

Для приема поступивших в сокет данных можно воспользоваться универсальной функцией низкоуровневого ввода/вывода read() или специализированным семейством функций recv*(), а для передачи - функцией write() или семейством send*(). Кроме того, функции select() и/или poll() помогут проверить наличие данных для приема или возможность отправки очередной порции данных.

Обращение к функции shutdown() завершает взаимодействие между партнерами.

1.2 Опрос данных о сети

Данные о хостах как узлах сети хранятся в сетевой базе, последовательный доступ к которой обслуживается функциями sethostent(), gethostent() и endhostent().

#include <netdb.h>

void sethostent (int stayopen);

struct hostent *gethostent (void);

void endhostent (void);

Листинг 11.1. Описание функций последовательного доступа к сетевой базе данных о хостах - узлах сети.

Функция sethostent() устанавливает соединение с базой, остающееся открытым после вызова gethostent(), если значение аргумента stayopen отлично от нуля. Функция gethostent() последовательно читает элементы базы, возвращая результат в структуре типа hostent, содержащей, по крайней мере следующие поля.

char *h_name;

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

char **h_aliases;

/* Массив указателей на альтернативные */

/* имена хоста, завершаемый пустым */

/* указателем */

int h_addrtype;

/* Тип адреса хоста */

int h_length;

/* Длина в байтах адреса данного типа */

char **h_addr_list;

/* Массив указателей на сетевые адреса */

/* хоста, завершаемый пустым указателем */

Функция endhostent() закрывает соединение с базой.

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

#include <stdio.h>

#include <netdb.h>

int main (void) {

struct hostent *pht;

char *pct;

int i, j;

sethostent (1);

while ((pht = gethostent ()) != NULL) {

printf ("Официальное имя хоста: %s\n", pht->h_name);

printf ("Альтернативные имена:\n");

for (i = 0; (pct = pht->h_aliases [i]) != NULL; i++) {

printf (" %s\n", pct);

}

printf ("Тип адреса хоста: %d\n", pht->h_addrtype);

printf ("Длина адреса хоста: %d\n", pht->h_length);

printf ("Сетевые адреса хоста:\n");

for (i = 0; (pct = pht->h_addr_list [i]) != NULL; i++) {

for (j = 0; j < pht->h_length; j++) {

printf (" %d", (unsigned char) pct [j]);

}

printf ("\n");

}

}

endhostent ();

return 0;

}

Листинг 11.2. Пример программы, осуществляющей последовательный доступ к сетевой базе данных о хостах - узлах сети.

Официальное имя хоста: localhost

Альтернативные имена:

Тип адреса хоста: 2

Длина адреса хоста: 4

Сетевые адреса хоста:

127 0 0 1

Официальное имя хоста: t01

Альтернативные имена:

niisi.msk.ru

t01.niisi.msk.ru

mail

mailhost

loghost

server

server3

server3.systud.msk.su

www

www.systud.msk.su

t01.systud.msk.su

Тип адреса хоста: 2

Длина адреса хоста: 4

Сетевые адреса хоста:

193 232 173 1

Официальное имя хоста: t17

Альтернативные имена:

galatenko

Тип адреса хоста: 2

Длина адреса хоста: 4

Сетевые адреса хоста:

193 232 173 17

Листинг 11.3. Фрагмент возможных результатов работы программы, осуществляющей последовательный доступ к сетевой базе данных о хостах - узлах сети.

К рассматриваемой базе возможен и случайный доступ по ключам - именам и адресам хостов с помощью функций gethostbyname() и gethostbyaddr(), однако они считаются устаревшими и из новой версии стандарта POSIX могут быть исключены. Вместо них предлагается использовать функции getnameinfo() и getaddrinfo() (см. пример 11.4).

#include <sys/socket.h>

#include <netdb.h>

void freeaddrinfo (struct addrinfo *ai);

int getaddrinfo

(const char *restrict nodename,

const char *restrict servname,

const struct addrinfo *restrict hints,

struct addrinfo **restrict res);

int getnameinfo

(const struct sockaddr *restrict sa,

socklen_t salen, char *restrict node,

socklen_t nodelen, char *restrict service,

socklen_t servicelen, int flags);

Листинг 11.4. Описание функций freeaddrinfo(), getaddrinfo(), getnameinfo().

Функция getaddrinfo() позволяет по имени узла сети (хоста) (аргумент nodename) и/или имени сетевого сервиса (servname) получить набор адресов сокетов и ассоциированную информацию, что дает возможность создать сокет для обращения к заданному сервису.

Если аргумент nodename отличен от пустого указателя, он способен задавать описательное имя или адресную цепочку. Для адресных семейств AF_INET и AF_UNSPEC (см. ниже описание аргумента hints) именем может служить имя хоста, а адресной цепочкой - стандартные для Internet адреса в точечных обозначениях (например, 193.232.173.17).

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

Аргумент servname может задавать имя сервиса или (для адресных семейств AF_INET и AF_UNSPEC) десятичный номер порта. Пустое значение servname означает запрос сетевого адреса.

Аргумент hints позволяет передать дополнительную информацию об опрашиваемом сервисе - адресное семейство, тип сокета, протокол, флаги. Согласно стандарту, структура addrinfo, описанная в заголовочном файле <netdb.h>, должна содержать по крайней мере следующие поля.

int ai_flags;

/* Входные флаги */

int ai_family;

/* Адресное семейство сокета */

int ai_socktype;

/* Тип сокета */

int ai_protocol;

/* Протокол сокета */

socklen_t ai_addrlen;

/* Длина адреса сокета */

struct sockaddr *ai_addr;

/* Адрес сокета */

char *ai_canonname;

/* Официальное имя узла сети */

struct addrinfo *ai_next;

/* Указатель на следующий элемент списка */

При обращении к функции getaddrinfo() все поля структуры addrinfo, на которую указывает аргумент hints, кроме первых четырех (ai_flags, ai_family, ai_socktype, ai_protocol), должны быть нулевыми или равными NULL. Значение AF_UNSPEC в поле ai_family подразумевает, что вызывающего устроит любое адресное семейство. Аналогичный смысл имеют нулевые значения полей ai_socktype и ai_protocol. При hints, равном NULL, подразумевается AF_UNSPEC для ai_family и нулевые значения для других полей.

Из флагов, которые могут быть установлены в поле ai_flags, упомянем следующие.

AI_PASSIVE

Если значение аргумента nodename равно NULL, этот флаг игнорируется. В противном случае, если он указан, будет возвращен адрес сокета, предназначенного для принятия входящих соединений.

AI_CANONNAME

Данный флаг предписывает выяснить официальное имя узла сети.

AI_NUMERICHOST

Флаг означает, что хост задан адресной цепочкой, и не допускает использования какого-либо сервиса имен.

AI_NUMERICSERV

Флаг помечает, что сервис (аргумент servname) задан номером порта, и налагает запрет на обращение к какому-либо сервису имен.

Признаком успешного завершения функции getaddrinfo() является нулевой результат. В таком случае выходной аргумент res будет ссылаться на указатель на список структур типа addrinfo (связанных полем ai_next) - принадлежащие им значения полей ai_family, ai_socktype, ai_protocol пригодны для создания подходящих сокетов с помощью функции socket(), а значения ai_addr и ai_addrlen, в зависимости от флага AI_PASSIVE, могут служить аргументами функций connect() или bind(), применяемых к созданному сокету.

Функция freeaddrinfo() позволяет освободить память, занятую списком структур типа addrinfo и ассоциированными данными.

Функцию getnameinfo() можно считать обратной по отношению к getaddrinfo(). Аргумент sa - входной, он задает транслируемый адрес сокета (salen - размер структуры sockaddr). Аргументы node и service - выходные, задающие адреса областей памяти, куда помещаются, соответственно, имя узла и сервиса; размеры этих областей ограничены значениями nodelen и servicelen.

По умолчанию предполагается, что сокет имеет тип SOCK_STREAM, а кроме того, возвращается полное доменное имя хоста. Аргумент flags позволяет изменить подразумеваемое поведение. Если задан флаг NI_DGRAM, сокет считается датаграммным. При установленном флаге NI_NOFQDN возвращается короткое имя узла. Флаги NI_NUMERICHOST и NI_NUMERICSERV предписывают возвращать числовые цепочки для адресов хоста и сервиса, соответственно.

Обратим внимание на несколько технических деталей. При работе с адресами сокетов вместо родовой структуры типа sockaddr обычно используются более специализированные (описанные в заголовочном файле <netinet/in.h>) - sockaddr_in для адресного семейства AF_INET (IPv4) и sockaddr_in6 для AF_INET6 (IPv6). Первая из них, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля (все с сетевым порядком байт).

sa_family_t sin_family; /* AF_INET */

in_port_t sin_port; /* Номер порта */

struct in_addr sin_addr; /* IP-адрес */

Структура типа sockaddr_in6 устроена несколько сложнее; мы не будем ее рассматривать.

Структуры типов sockaddr и sockaddr_in (или sockaddr_in6) мысленно накладываются друг на друга, а преобразование типов между ними выполняется по мере необходимости. Отметим также, что тип in_port_t эквивалентен uint16_t, структура типа in_addr содержит по крайней мере одно поле:

in_addr_t s_addr;

тип in_addr_t эквивалентен uint32_t.

Техническую роль играют и функции преобразования IP-адресов из текстового представления в числовое и наоборот (см. пример 11.5).

#include <arpa/inet.h>

in_addr_t inet_addr (const char *cp);

char *inet_ntoa (struct in_addr in);

int inet_pton (int af, const char

*restrict src, void *restrict dst);

const char *inet_ntop (int af, const void

*restrict src, char *restrict dst,

socklen_t size);

Листинг 11.5. Описание функций преобразования IP-адресов из текстового представления в числовое и наоборот.

Первые две функции манипулируют только адресами IPv4: inet_addr() преобразует текстовую цепочку cp (адрес в стандартных точечных обозначениях) в пригодное для использования в качестве IP-адреса целочисленное значение, inet_ntoa() выполняет обратное преобразование.

Вторая пара функций по сути аналогична первой, но имеет чуть более общий характер, так как способна преобразовывать адреса в формате IPv6. Первый аргумент этих функций, af, задает адресное семейство: AF_INET для IPv4 и AF_INET6 для IPv6. Буфер, на который указывает аргумент dst функции inet_pton() (в него помещается результат преобразования - IP-адрес в числовой двоичной форме с сетевым порядком байт), должен иметь длину не менее 32 бит для адресов IPv4 и 128 бит для IPv6.

Аргумент src функции inet_ntop(), возвращающей текстовое представление, указывает на буфер с IP-адресом в числовой форме с сетевым порядком байт. Аргумент size задает длину выходного буфера, на него указывает аргумент dst. Подходящими значениями, в зависимости от адресного семейства, могут служить INET_ADDRSTRLEN или INET6_ADDRSTRLEN.

Выше было отмечено, что преобразование значений типов uint16_t и uint32_t из хостового порядка байт в сетевой выполняется посредством функций htons() и htonl(); функции ntohs() и ntohl() осуществляют обратную операцию (см. пример 11.6).

#include <arpa/inet.h>

uint32_t htonl (uint32_t hostlong);

uint16_t htons (uint16_t hostshort);

uint32_t ntohl (uint32_t netlong);

uint16_t ntohs (uint16_t netshort);

Листинг 11.6. Описание функций преобразования целочисленных значений из хостового порядка байт в сетевой и наоборот.

Использование функции getaddrinfo() вместе с сопутствующими техническими деталями проиллюстрируем программой, показанной в пример 11.7. Возможные результаты ее выполнения приведены в пример 11.8. Использование функции getaddrinfo() вместе с сопутствующими техническими деталями проиллюстрируем программой, показанной в пример 11.7. Возможные результаты ее выполнения приведены в пример 11.8.

#include <stdio.h>

#include <netdb.h>

#include <arpa/inet.h>

int main (void) {

struct addrinfo hints = {AI_CANONNAME, AF_INET, SOCK_STREAM, IPPROTO_TCP,

0, NULL, NULL, NULL};

struct addrinfo *addr_res;

if (getaddrinfo ("www", "http", &hints, &addr_res) != 0) {

perror ("GETADDRINFO");

} else {

printf ("Результаты для сервиса http\n");

/* Пройдем по списку возвращенных структур */

do {

printf ("Адрес сокета: Порт: %d IP-адрес: %s\n",

ntohs (((struct sockaddr_in *) addr_res->ai_addr)->sin_port),

inet_ntoa (((struct sockaddr_in *) addr_res->ai_addr)->sin_addr));

printf ("Официальное имя хоста: %s\n", addr_res->ai_canonname);

} while ((addr_res = addr_res->ai_next) != NULL);

return 0;

}

Листинг 11.7. Пример программы, использующей функцию getaddrinfo().

Результаты для сервиса http

Адрес сокета: Порт: 80 IP-адрес: 193.232.173.1

Официальное имя хоста: t01

Листинг 11.8. Возможный результат работы программы, использующей функцию getaddrinfo().

Завершая изложение серии технических моментов, укажем, что полезным дополнением к функциям getaddrinfo() и getnameinfo() является функция gai_strerror() (см. пример 11.9). Она возвращает текстовую цепочку, расшифровывающую коды ошибок, перечисленные в заголовочном файле <netdb.h>. Стандарт POSIX-2001 специфицирует следующие коды ошибок, имена которых говорят сами за себя: EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NONAME, EAI_OVERFLOW, EAI_SERVICE, EAI_SOCKTYPE, EAI_SYSTEM.

#include <netdb.h>

const char *gai_strerror (int ecode);

Листинг 11.9. Описание функции gai_strerror().

Если немного модифицировать приведенную выше программу (в пример 11.10 показан измененный фрагмент, где имя сервиса - "HTTP" - задано большими буквами), то в стандартный протокол с помощью функции gai_strerror() будет выдано содержательное диагностическое сообщение (см. пример 11.11). Отметим, что выдача функции perror() в данном случае невразумительна.

int res;

if ((res = getaddrinfo ("www", "HTTP", &hints, &addr_res)) != 0) {

perror ("GETADDRINFO");

fprintf (stderr, "GETADDRINFO: %s\n", gai_strerror (res));

} else {

printf ("Результаты для сервиса HTTP\n");

Листинг 11.10. Модифицированный фрагмент программы, использующей функции getaddrinfo() и gai_strerror().

GETADDRINFO: No such file or directory

GETADDRINFO: Servname not supported for ai_socktype

Листинг 11.11. Диагностические сообщения от функций perror() и gai_strerror(), выданные по результатам работы функции getaddrinfo().

Наряду с базой данных хостов (узлов сети) поддерживается база данных сетей с аналогичной логикой работы и набором функций (см. пример 11.12).

#include <netdb.h>

void setnetent (int stayopen);

struct netent *getnetent (void);

struct netent *getnetbyaddr (uint32_t net,

int type);

struct netent *getnetbyname (const char *name);

void endnetent (void);

Листинг 11.12. Описание функций доступа к базе данных сетей.

Функция getnetent() обслуживает последовательный доступ к базе, getnetbyaddr() осуществляет поиск по адресному семейству (аргумент type) и номеру net сети, а getnetbyname() выбирает сеть с заданным (официальным) именем. Структура типа netent, указатель на которую возвращается в качестве результата этих функций, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля.

char *n_name;

/* Официальное имя сети */

char **n_aliases;

/* Массив указателей на альтернативные */

/* имена сети, завершаемый пустым указателем */

int n_addrtype;

/* Адресное семейство (тип адресов) сети */

uint32_t n_net;

/* Номер сети (в хостовом порядке байт) */

Точно такой же программный интерфейс предоставляет база данных сетевых протоколов (см. пример 11.13).

#include <netdb.h>

void setprotoent (int stayopen);

struct protoent *getprotoent (void);

struct protoent *getprotobyname

(const char *name);

struct protoent *getprotobynumber (int proto);

void endprotoent (void);

Листинг 11.13. Описание функций доступа к базе данных сетевых протоколов.

Структура типа protoent содержит, по крайней мере, следующие поля.

char *p_name;

/* Официальное имя протокола */

char **p_aliases;

/* Массив указателей на альтернативные */

/* имена протокола, завершаемый пустым */

/* указателем */

int p_proto;

/* Номер протокола */

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

#include <stdio.h>

#include <netdb.h>

int main (void) {

struct protoent *pht;

char *pct;

int i;

setprotoent (1);

while ((pht = getprotoent ()) != NULL) {

printf ("Официальное имя протокола: %s\n", pht->p_name);

printf ("Альтернативные имена:\n");

for (i = 0; (pct = pht->p_aliases [i]) != NULL; i++) {

printf (" %s\n", pct);

}

printf ("Номер протокола: %d\n\n", pht->p_proto);

}

if ((pht = getprotobyname ("ipv6")) != NULL) {

printf ("Номер протокола ipv6: %d\n\n", pht->p_proto);

} else {

fprintf (stderr, "Протокол ip в базе не найден\n");

}

if ((pht = getprotobyname ("IPV6")) != NULL) {

printf ("Номер протокола IPV6: %d\n\n", pht->p_proto);

} else {

fprintf (stderr, "Протокол IPV6 в базе не найден\n");

}

endprotoent ();

return 0;

}

Листинг 11.14. Пример программы, осуществляющей последовательный и случайный доступ к базе данных сетевых протоколов.

Официальное имя протокола: ip

Альтернативные имена:

IP

Номер протокола: 0

Официальное имя протокола: icmp

Альтернативные имена:

ICMP

Номер протокола: 1

Официальное имя протокола: tcp

Альтернативные имена:

TCP

Номер протокола: 6

Официальное имя протокола: udp

Альтернативные имена:

UDP

Номер протокола: 17

Официальное имя протокола: ipv6

Альтернативные имена:

IPv6

Номер протокола: 41

Официальное имя протокола: ipv6-crypt

Альтернативные имена:

IPv6-Crypt

Номер протокола: 50

Официальное имя протокола: visa

Альтернативные имена:

VISA

Номер протокола: 70

Официальное имя протокола: iso-ip

Альтернативные имена:

ISO-IP

Номер протокола: 80

Официальное имя протокола: sprite-rpc

Альтернативные имена:

Sprite-RPC

Номер протокола: 90

...

Официальное имя протокола: ipx-in-ip

Альтернативные имена:

IPX-in-IP

Номер протокола: 111

...

Официальное имя протокола: fc

Альтернативные имена:

FC

Номер протокола: 133

Номер протокола ipv6: 41

Протокол IPV6 в базе не найден

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

Еще одно проявление той же логики работы - база данных сетевых сервисов (см. пример 11.16).

#include <netdb.h>

void setservent (int stayopen);

struct servent *getservent (void);

struct servent *getservbyname

(const char *name, const char *proto);

struct servent *getservbyport

(int port, const char *proto);

void endservent (void);

Листинг 11.16. Описание функций доступа к базе данных сетевых сервисов.

Обратим внимание на то, что в данном случае можно указывать второй аргумент поиска - имя протокола. Впрочем, значение аргумента proto может быть пустым указателем, и тогда поиск производится только по имени сервиса (функция getservbyname()) или номеру порта (getservbyport()), который должен быть задан с сетевым порядком байт.

Структура типа servent содержит по крайней мере следующие поля.

char *s_name;

/* Официальное имя сервиса */

char **s_aliases;

/* Массив указателей на альтернативные */

/* имена сервиса, завершаемый пустым */

/* указателем */

int s_port;

/* Номер порта, соответствующий сервису */

/* (в сетевом порядке байт) */

char *s_proto;

/* Имя протокола для взаимодействия с */

/* сервисом */

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

#include <stdio.h>

#include <netdb.h>

int main (void) {

struct servent *pht;

char *pct;

int i;

setservent (1);

while ((pht = getservent ()) != NULL) {

printf ("Официальное имя сервиса: %s\n", pht->s_name);

printf ("Альтернативные имена:\n");

for (i = 0; (pct = pht->s_aliases [i]) != NULL; i++) {

printf (" %s\n", pct);

}

printf ("Номер порта: %d\n", ntohs ((in_port_t) pht->s_port));

printf ("Имя протокола: %s\n\n", pht->s_proto);

}

if ((pht = getservbyport (htons ((in_port_t) 21), "udp")) != NULL) {

printf ("Официальное имя сервиса: %s\n", pht->s_name);

printf ("Альтернативные имена:\n");

for (i = 0; (pct = pht->s_aliases [i]) != NULL; i++) {

printf (" %s\n", pct);

}

printf ("Номер порта: %d\n", ntohs ((in_port_t) pht->s_port));

printf ("Имя протокола: %s\n\n", pht->s_proto);

} else {

perror ("GETSERVBYPORT");

}

if ((pht = getservbyport (htons ((in_port_t) 21), (char *) NULL)) != NULL) {

printf ("Официальное имя сервиса: %s\n", pht->s_name);

printf ("Альтернативные имена:\n");

for (i = 0; (pct = pht->s_aliases [i]) != NULL; i++) {

printf (" %s\n", pct);

}

printf ("Номер порта: %d\n", ntohs ((in_port_t) pht->s_port));

printf ("Имя протокола: %s\n\n", pht->s_proto);

} else {

perror ("GETSERVBYPORT");

}

endservent ();

return 0;

}

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

Официальное имя сервиса: ftp-data

Альтернативные имена:

Номер порта: 20

Имя протокола: tcp

Официальное имя сервиса: ftp-data

Альтернативные имена:

Номер порта: 20

Имя протокола: udp

Официальное имя сервиса: ftp

Альтернативные имена:

Номер порта: 21

Имя протокола: tcp

Официальное имя сервиса: ftp

Альтернативные имена:

fsp

fspd

Номер порта: 21

Имя протокола: udp

...

Официальное имя сервиса: kerberos

Альтернативные имена:

kerberos5

krb5

Номер порта: 88

Имя протокола: tcp

Официальное имя сервиса: kerberos

Альтернативные имена:

kerberos5

krb5

Номер порта: 88

Имя протокола: udp

...

Официальное имя сервиса: auth

Альтернативные имена:

authentication

tap

ident

Номер порта: 113

Имя протокола: tcp

Официальное имя сервиса: auth

Альтернативные имена:

authentication

tap

ident

Номер порта: 113

Имя протокола: udp

...

Официальное имя сервиса: printer

Альтернативные имена:

spooler

Номер порта: 515

Имя протокола: tcp

Официальное имя сервиса: printer

Альтернативные имена:

spooler

Номер порта: 515

Имя протокола: udp

...

Официальное имя сервиса: fido

Альтернативные имена:

Номер порта: 60179

Имя протокола: tcp

Официальное имя сервиса: fido

Альтернативные имена:

Номер порта: 60179

Имя протокола: udp

Официальное имя сервиса: ftp

Альтернативные имена:

fsp

fspd

Номер порта: 21

Имя протокола: udp

Официальное имя сервиса: ftp

Альтернативные имена:

Номер порта: 21

Имя протокола: tcp

Листинг 11.18. Фрагмент возможных результатов работы программы, использующей функции доступа к базе данных сервисов, а также функции преобразования между хостовым и сетевым порядками байт.

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

1.3 Функции для работы с сокетами

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

#include <sys/socket.h>

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

int socketpair (int af, int type,

int protocol, int sds [2]);

Листинг 11.19. Описание функций socket() и socketpair().

Аргумент af задает адресное семейство создаваемого сокета, аргумент type - тип, аргумент protocol - конкретный протокол (0 обозначает неспецифицированный подразумеваемый протокол, пригодный для запрошенного типа). Напомним, что подходящие значения для аргументов af, type и protocol можно получить с помощью описанной ранее функции getaddrinfo().

Функция socketpair() по назначению аналогична pipe(), только организуется не безымянный канал, а пара соединенных, безымянных (не привязанных к каким-либо адресам), идентичных сокетов, открытые файловые дескрипторы которых помещаются в массив sds. Обычно она используется для адресного семейства AF_UNIX; поддержка для других семейств не гарантируется.

После того как посредством функции bind() (см. пример 11.20) создан сокет, идентифицируемый дескриптором sd, ему присваивают локальный адрес, заданный аргументом address (address_len - длина структуры sockaddr, на которую указывает address). Источником локальных адресов для сокетов может служить вышеупомянутая функция getaddrinfo().

#include <sys/socket.h>

int bind (int sd, const struct sockaddr

*address, socklen_t address_len);

Листинг 11.20. Описание функции bind().

Опросить присвоенный локальный адрес (его иногда называют именем сокета) можно с помощью функции getsockname() (см. пример 11.21): она помещает его в структуру sockaddr, на которую указывает аргумент address, а длину адреса записывает по указателю address_len.

#include <sys/socket.h>

int getsockname

(int sd, struct sockaddr *restrict address,

socklen_t *restrict address_len);

Листинг 11.21. Описание функции getsockname().

Если сокет ориентирован на режим с установлением соединения (имеет тип SOCK_STREAM), то, воспользовавшись функцией listen() (см. пример 11.22), его следует пометить как готового принимать соединения ("слушающего").

#include <sys/socket.h>

int listen (int sd, int backlog);

Листинг 11.22. Описание функции listen().

Аргумент backlog сообщает операционной системе рекомендуемую длину очереди соединений, ожидающих обработки слушающим сокетом. Реализация должна поддерживать значения аргумента backlog вплоть до конфигурационной константы SOMAXCONN, определенной в заголовочном файле <sys/socket.h>. ОС имеет право установить длину очереди меньше рекомендуемой. При неположительном значении backlog очередь будет иметь зависящую от реализации минимальную длину.

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

#include <sys/socket.h>

int accept

(int sd, struct sockaddr *restrict address,

socklen_t *restrict address_len);

Листинг 11.23. Описание функции accept().

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

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

Другая сторона, т. е. потенциальный партнер по взаимодействию, инициирует соединение с помощью функции connect() (см. пример 11.24). Аргументы address и address_len стандартным образом задают адрес сокета (как правило, слушающего), с которым необходимо установить соединение.

#include <sys/socket.h>

int connect (int sd, const struct sockaddr

*address, socklen_t address_len);

Листинг 11.24. Описание функции connect().

Если для сокета, заданного аргументом sd (запрашивающего установление соединения), еще не выполнена привязка к локальному адресу, функция connect() сама осуществит связывание со свободным локальным адресом (правда, лишь при условии, что адресное семейство сокета отлично от AF_UNIX).

Функция connect() ограничится фиксацией адреса сокета, взаимодействующего с заданным аргументом sd, если тип сокета не требует установления соединения. В частности, для сокетов типа SOCK_DGRAM таким способом можно специфицировать адреса отправляемых (с помощью функции send()) и принимаемых (посредством обращения к функции recv()) датаграмм.

Когда в качестве аргумента address передается пустой указатель, адрес взаимодействующего сокета сбрасывается.

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

Если для дескриптора sd задан флаг O_NONBLOCK, а соединение не может быть установлено немедленно, то вызов connect() завершается неудачей со значением errno, равным EINPROGRESS, но установление соединения продолжается и будет завершено асинхронно. Последующие обращения к функции connect() с тем же сокетом, выполненные до установления соединения, завершаются неудачей (EALREADY).

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

После асинхронного установления соединения функции select() и poll() сообщат, что файловый дескриптор sd готов к записи.

Функция poll(), позволяющая мультиплексировать ввод/вывод в пределах набора файловых дескрипторов, была описана нами выше. Имеющая сходную направленность (но входящая в обязательную часть стандарта POSIX-2001) функция select() и ее недавно введенный аналог pselect() представлены в пример 11.25.

#include <sys/select.h>

int pselect (int nfds, fd_set *restrict readfds, fd_set *restrict writefds,

fd_set *restrict errorfds, const struct timespec *restrict timeout,

const sigset_t *restrict sigmask);

int select (int nfds, fd_set *restrict readfds, fd_set *restrict writefds,

fd_set *restrict errorfds, struct timeval *restrict timeout);

void FD_CLR (int fd, fd_set *fdset);

int FD_ISSET (int fd, fd_set *fdset);

void FD_SET (int fd, fd_set *fdset);

void FD_ZERO (fd_set *fdset);

Листинг 11.25. Описание функций семейства select*().

Если значение аргумента readfds (writefds, errorfds) функции pselect() отлично от NULL, оно ссылается на объект типа fd_set, который на входе специфицирует набор файловых дескрипторов, проверяемых на готовность к чтению (или к записи, или к обработке исключительных ситуаций), а на выходе указывает, какие из них успешно прошли проверку. Аргумент nfds задает границу проверяемых дескрипторов (они являются небольшими целыми числами): дескриптор подлежит проверке, если его значение находится в пределах от 0 до (nfds - 1) включительно.

Стандарт POSIX-2001 определяет тип fd_set как абстрактный. Для работы со значениями этого типа служат макросы FD_ZERO() (сделать набор пустым), FD_SET() (добавить дескриптор к набору), FD_CLR() (удалить дескриптор из набора), FD_ISSET() (проверить принадлежность дескриптора набору). Значение именованной константы FD_SETSIZE равно максимально допустимому числу дескрипторов в наборе.

В качестве результата pselect() возвращается общее количество дескрипторов во всех трех наборах, успешно прошедших проверку.

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

Если значение аргумента sigmask отлично от пустого указателя, функция pselect() на входе заменяет маску сигналов процесса на заданную, а на выходе восстанавливает прежнюю маску.

Функция pselect() поддерживает обычные файлы, терминалы и псевдотерминалы, каналы и сокеты.

Функция select() эквивалентна pselect() со следующими оговорками.

1. Для функции select() время ожидания задается в секундах и микросекундах в виде структуры типа timeval, а для pselect() - в секундах и наносекундах как аргумент типа struct timespec.

2. У функции select() нет аргумента - маски сигналов, что эквивалентно пустому указателю в качестве значения аргумента sigmask функции pselect().

3. В случае успешного завершения функция select() может модифицировать структуру, на которую указывает аргумент timeout.

С сокетами могут быть ассоциированы опции, влияющие на их функционирование. Значения этих опций можно опросить или изменить с помощью функций getsockopt() и setsockopt() (см. пример 11.26).

#include <sys/socket.h>

int getsockopt (int sd, int level,

int option_name,

void *restrict option_value,

socklen_t *restrict option_len);

int setsockopt (int sd, int level,

int option_name,

const void *option_value,

socklen_t option_len);

Листинг 11.26. Описание функций getsockopt() и setsockopt().

Опции задаются именованными константами (аргумент option_name), которые определены в заголовочном файле <sys/socket.h>. Выделим среди них наиболее употребительные и разделим на несколько групп. К первой отнесем опции с целочисленными значениями, описывающими характеристики или состояние сокета.

SO_ERROR

Статус ошибок (после опроса очищается).

SO_TYPE

Тип сокета.

Ко второй группе отнесем булевы опции, представленные целочисленными значениями (0 означает ложь).

SO_DEBUG

Сообщает, записывается ли отладочная информация о работе сокета.

SO_ACCEPTCONN

Указывает, является ли сокет слушающим.

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

SO_SNDBUF

Размер буфера для передаваемых данных (выходного буфера).

SO_RCVBUF

Размер входного буфера.

SO_RCVLOWATM

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

SO_SNDLOWAT

Минимальное число байт, обрабатываемых при выводе.

В четвертую группу входят опции со структурными значениями.

SO_LINGER

Определяет, блокировать ли процесс при закрытии дескриптора sd до передачи буферизованных данных, и если блокировать, то на какой срок. Значением опции является структура linger, определенная в заголовочном файле <sys/socket.h> и содержащая, согласно стандарту POSIX-2001, по крайней мере следующие поля.

int l_onoff;

/* Признак, включена ли опция блокирования */

/* при закрытии */

int l_linger;

/* Длительность блокирования в секундах */

SO_RCVTIMEO

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

SO_SNDTIMEO

Длительность ожидания отправки данных при выводе.

Не все из перечисленных опций могут быть переустановлены функцией setsockopt(). Исключение по понятным причинам составляют SO_ERROR, SO_TYPE, SO_ACCEPTCONN.

Аргумент level задает протокольный уровень опции. Уровню сокетов соответствует значение SOL_SOCKET, уровню TCP - IPPROTO_TCP.

Функция getpeername() (см. пример 11.27), во многом аналогичная рассмотренной выше функции getsockname(), позволяет опросить еще одну характеристику - адрес (имя) сокета, с которым установлено соединение.

#include <sys/socket.h>

int getpeername (int sd,

struct sockaddr *restrict address,

socklen_t *restrict address_len);

Листинг 11.27. Описание функции getpeername().

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

#include <sys/socket.h>

ssize_t recvfrom

(int sd, void *restrict buffer,

size_t length, int flags,

struct sockaddr *restrict address,

socklen_t *restrict address_len);

ssize_t recv (int sd, void *buffer,

size_t length, int flags);

ssize_t recvmsg (int sd, struct msghdr

*message, int flags);

ssize_t sendto

(int sd, const void *message,

size_t length, int flags,

const struct sockaddr *dest_addr,

socklen_t dest_len);

ssize_t send (int sd, const void *buffer,

size_t length, int flags);

ssize_t sendmsg (int sd, const struct msghdr

*message, int flags);

Листинг 11.28. Описание функций обмена данными через сокет.

Функция recvfrom() позволяет прочитать данные (в рассматриваемом контексте называемые также сообщением) из сокета с дескриптором sd и поместить их в буфер buffer длины length. Обычно функцию recvfrom() применяют к сокетам, ориентированным на режим без установления соединения, поскольку она выдает исходный адрес в структуре типа sockaddr.

В число возможных составляющих значения аргумента flags входят следующие флаги.

MSG_PEEK

Не удалять прочитанные данные. Следующий вызов recvfrom() или другой функции ввода снова вернет их.

MSG_WAITALL

Для сокетов типа SOCK_STREAM флаг означает, что вызывающий процесс блокируется до получения всего запрошенного объема данных (а не до прихода первого сообщения).

MSG_OOB

Запрашиваются экстренные данные. Трактовка этого понятия зависит от протокола.

Как и положено функции ввода, в результате recvfrom() возвращает количество прочитанных и помещенных в буфер данных. Для сокетов, учитывающих границы сообщений (SOCK_RAW, SOCK_DGRAM, SOCK_SEQPACKET), за одно обращение читается все сообщение; если оно не помещается в буфер, лишние байты (в отсутствие флага MSG_PEEK) отбрасываются.

Формально можно считать, что функция recv() эквивалентна recvfrom() с нулевым значением аргумента address_len. Поскольку она не позволяет узнать исходный адрес, ее обычно используют для сокетов, установивших соединение. (Можно провести и еще одну аналогию: если пренебречь флагами, функция recv() эквивалентна read().)

По сравнению с recv(), более содержательным аналогом функции recvfrom() является recvmsg(). Отличия в данном случае носят скорее синтаксический характер и по сути сводятся к способу передачи входных значений и возврата результатов: для минимизации числа аргументов функция recvmsg() использует структуру типа msghdr, которая, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля.

void *msg_name;

/* Указатель на буфер для адреса */

/* (исходного или целевого) */

socklen_t msg_namelen;

/* Размер буфера для адреса */

struct iovec *msg_iov;

/* Массив для разнесения/сборки сообщений */

int msg_iovlen;

/* Число элементов в массиве */

/* для разнесения/сборки сообщений */

void *msg_control;

/* Указатель на буфер */

/* для вспомогательных данных */

socklen_t msg_controllen;

/* Размер буфера для вспомогательных данных */

int msg_flags;

/* Флаги принятого сообщения */

Если для сокета, специфицированного дескриптором sd, соединение не установлено, в заданный указателем msg_name буфер длины msg_namelen помещается исходный адрес сообщения; если этого не нужно, указатель msg_name можно сделать пустым. При установлении соединения оба поля игнорируются.

Массив msg_iov совместно с полем msg_iovlen задает набор буферов для размещения принимаемых данных. Структура типа iovec определяется в заголовочном файле <sys/uio.h> и содержит по крайней мере два поля.

void *iov_base;

/* Адрес области памяти (буфера) */

size_t iov_len;

/* Размер области памяти (в байтах) */

Заданные полем msg_iov области памяти по очереди заполняются поступающими данными, пока не будут размещены все принятые данные или не заполнятся все буфера.

Трактовка вспомогательных данных (поля msg_control и msg_controllen структуры типа msghdr) в стандарте POSIX-2001 весьма туманна. Описаны лишь средства доступа к ним. Мы, однако, не будем на этом останавливаться.

В случае успешного завершения функции recvmsg() в поле msg_flags могут быть установлены следующие флаги.

MSG_EOR

Достигнута граница записи (если это понятие поддерживается протоколом).

MSG_OOB

Получены экстренные данные.

MSG_TRUNC

Пришлось урезать обычные данные.

MSG_CTRUNC

Пришлось урезать управляющие данные.

Подобно тому, как write() составляет пару read(), функцию sendto() можно считать парной по отношению к recvfrom(). Она предназначена для отправки через сокет sd сообщения, заданного аргументами message и length, по адресу dest_addr длины dest_len. Если для сокета установлено соединение, целевой адрес dest_addr игнорируется.

В число возможных составляющих значения аргумента flags входят следующие флаги.

MSG_EOR

Обозначает границу записи (если это понятие поддерживается протоколом).

MSG_OOB

Отправляются экстренные данные.

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

Функция send() - пара для recv() со всеми следующими из этого обстоятельства эквивалентностями и аналогиями.

Для функции sendmsg() структура типа msghdr, на которую указывает аргумент message, является входной (что показывает спецификатор const). В поле msg_name задается целевой адрес. Поле msg_flags игнорируется.

Для завершения операций приема и/или отправки данных через сокет служит функция shutdown() (см. пример 11.29).

#include <sys/socket.h>

int shutdown (int sd, int how);

Листинг 11.29. Описание функции shutdown().

Значение аргумента how показывает, что именно завершается: SHUT_RD прекращает прием, SHUT_WR - отправку, SHUT_RDWR - и то, и другое.

1.4 Примеры программ работы с сокетами

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

/* Программа копирует строки со стандартного ввода на стандартный вывод, */

/* "прокачивая" их через пару сокетов. */

/* Используются функции ввода/вывода нижнего уровня */

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <sys/socket.h>

#include <sys/wait.h>

#define MY_PROMPT "Вводите строки\n"

#define MY_MSG "Вы ввели:

int main (void) {

int sds [2];

char buf [1];

int new_line = 1; /* Признак того, что надо выдать сообщение MY_MSG */

/* перед отображением очередной строки */

/* Создадим пару соединенных безымянных сокетов */

if (socketpair (AF_UNIX, SOCK_STREAM, 0, sds) < 0) {

perror ("SOCKETPAIR");

exit (1);

}

switch (fork ()) {

case -1:

perror ("FORK");

exit (2);

case 0:

/* Чтение из сокета sds [0] и выдачу на стандартный вывод */

/* реализуем в порожденном процессе */

while (read (sds [0], buf, 1) == 1) {

if (write (STDOUT_FILENO, buf, 1) != 1) {

perror ("WRITE TO STDOUT");

break;

}

}

exit (0);

}

/* Чтение со стандартного ввода и запись в сокет sds [1] */

/* возложим на родительский процесс */

if (write (sds [1], MY_PROMPT, sizeof (MY_PROMPT) - 1) !=

sizeof (MY_PROMPT) - 1) {

perror ("WRITE TO SOCKET-1");

}

while (read (STDIN_FILENO, buf, 1) == 1) {

/* Перед отображением очередной строки */

/* нужно выдать сообщение MY_MSG */

if (new_line) {

if (write (sds [1], MY_MSG, sizeof (MY_MSG) - 1) != sizeof (MY_MSG) - 1) {

perror ("WRITE TO SOCKET-2");

break;

}

if (write (sds [1], buf, 1) != 1) {

perror ("WRITE TO SOCKET-3");

break;

}

new_line = (buf [0] == '\n');

}

shutdown (sds [1], SHUT_WR);

(void) wait (NULL);

return (0);

}

Листинг 11.30. Пример программы, использующей сокеты адресного семейства AF_UNIX.

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

Решим теперь ту же задачу для действительно распределенной конфигурации. Наше приложение будет состоять из двух процессов, выполняющихся на разных хостах. Первый процесс (см. пример 11.31) читает строки со стандартного ввода и отправляет их в виде датаграмм другому, который принимает датаграммы и выдает их содержимое на стандартный вывод (см. пример 11.32).

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Программа процесса, читающего строки со стандартного ввода */

/* и посылающего их в виде датаграмм другому процессу */

/* (будем называть его сервером), */

/* который должно выдать их на свой стандартный вывод. */

/* Имя серверного хоста - аргумент командной строки */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h>

#include <netdb.h>

#include <sys/socket.h>

#include <string.h>

#define MY_PROMPT "Вводите строки\n"

int main (int argc, char *argv []) {

int sd; /* Дескриптор передающего сокета */

char line [LINE_MAX]; /* Буфер для копируемых строк */


Подобные документы

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

    курсовая работа [87,2 K], добавлен 13.09.2010

  • Характеристика и правила организации баз данных - совокупности взаимосвязанных данных, характеризующихся возможностью использования для большого количества приложений; возможностью быстрого получения и модификации необходимой информации. Выбор СУБД.

    реферат [1,4 M], добавлен 27.10.2010

  • Особенности организации передачи данных в компьютерной сети. Эталонная модель взаимодействия открытых систем. Методы передачи данных на нижнем уровне, доступа к передающей среде. Анализ протоколов передачи данных нижнего уровня на примере стека TCP/IP.

    курсовая работа [1,0 M], добавлен 07.08.2011

  • Что такое локальная сеть. Аппаратные средства компьютерных сетей, физические, логические топологии локальных вычислительных сетей. Передача информации по волоконно-оптическим кабелям, коммуникационная аппаратура. Концепции управления сетевыми ресурсами.

    курсовая работа [917,3 K], добавлен 22.09.2009

  • Основная цель и модели сети. Принцип построения ее соединений. Технология клиент-сервер. Характеристика сетевых архитектур Ethernet, Token Ring, ArcNet: метод доступа, среда передачи, топология. Способы защиты информации. Права доступа к ресурсам сети.

    презентация [269,0 K], добавлен 26.01.2015

  • Общие понятия компьютерных сетей. Протоколы и их взаимодействие. Базовые технологии канального уровня. Сетевые устройства физического и канального уровня. Характеристика уровней модели OSI. Глобальные компьютерные сети. Использование масок в IP-адресации.

    курс лекций [177,8 K], добавлен 16.12.2010

  • Классификация компьютерных сетей. Взаимодействие компьютеров в сети. Сетевые модели и архитектуры. Мосты и коммутаторы, сетевые протоколы. Правила назначения IP-адресов сетей и узлов. Сетевые службы, клиенты, серверы, ресурсы. Способы доступа в Интернет.

    курсовая работа [1,5 M], добавлен 11.05.2014

  • Всемирная тенденция к объединению компьютеров в сети. Компьютерные сети: основные типы и устройство. Глобальная сеть Интернет. Современные сетевые технологи в компьютерных сетях. Особенности технологии Wi-Fi, IP-телефония. Виртуальные частные сети.

    презентация [648,3 K], добавлен 14.02.2016

  • Современные средства связи и их характеристика. Разработка структуры сети передачи данных. Выбор типа доступа. Основные уровни модели OSI, технология доступа. Выбор оборудования, характеристики сервера. Расчет стоимостных показателей для прокладки сети.

    курсовая работа [411,7 K], добавлен 22.04.2013

  • Сетевые операционные системы, их характеристика и виды. Функции программного обеспечения локальной компьютерной сети. Структура и функции прокси-сервера и межсетевого экрана. Базы данных в локальных сетях, электронная почта, системы удаленного доступа.

    курсовая работа [43,9 K], добавлен 21.07.2012

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.