учебники, программирование, основы, введение в,

 

Сетевые средства

Основные понятия и объекты
Стандарт 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() завершает взаимодействие между партнерами.
Опрос данных о сети
Данные о хостах как узлах сети хранятся в сетевой базе, последовательный доступ к которой обслуживается функциями 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. Фрагмент возможных результатов работы программы, осуществляющей последовательный доступ к сетевой базе данных о хостах - узлах сети.
К рассматриваемой базе возможен и случайный доступ по ключам - именам и адресам хостов с помощью функций gethostbyname() и gethostbyaddr(), однако они считаются устаревшими и из новой версии стандарта POSIX могут быть исключены. Вместо них предлагается использовать функции getnameinfo() и getaddrinfo().
#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) задан номером порта, и налагает запрет на обращение к какому-либо сервису имен.
http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gifПризнаком успешного завершения функции 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-адресов из текстового представления в числовое и наоборот (см.).
#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() осуществляют обратную операцию.
#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. Пример программы, использующей функцию getaddrinfo().
Листинг 11.8. Возможный результат работы программы, использующей функцию getaddrinfo().
Завершая изложение серии технических моментов, укажем, что полезным дополнением к функциям getaddrinfo() и getnameinfo() является функция gai_strerror() ). Она возвращает текстовую цепочку, расшифровывающую коды ошибок, перечисленные в заголовочном файле <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().
Если немного модифицировать приведенную выше программу показан измененный фрагмент, где имя сервиса - "HTTP" - задано большими буквами), то в стандартный протокол с помощью функции gai_strerror() будет выдано содержательное диагностическое сообщение . Отметим, что выдача функции perror() в данном случае невразумительна.
Листинг 11.10. Модифицированный фрагмент программы, использующей функции getaddrinfo() и gai_strerror().
Листинг 11.11. Диагностические сообщения от функций perror() и gai_strerror(), выданные по результатам работы функции getaddrinfo().
Наряду с базой данных хостов (узлов сети) поддерживается база данных сетей с аналогичной логикой работы и набором функций.
#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;        
/* Номер сети (в хостовом порядке байт)      */
Точно такой же программный интерфейс предоставляет база данных сетевых протоколов.
#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 <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. Фрагмент возможных результатов работы программы, использующей функции доступа к базе данных сервисов, а также функции преобразования между хостовым и сетевым порядками байт.
Отметим, что при поиске по ключу возвращается первый подходящий элемент базы данных. По этой причине, когда не был задан протокол (второе обращение к функции getservbyport()), в качестве результата был возвращен элемент с протоколом tcp.

http://localhost:3232/img/empty.gifФункции для работы с сокетами

Работа с сокетами как оконечными точками при взаимодействии процессов начинается с их (сокетов) создания посредством функции socket() . Она возвращает открытый файловый дескриптор, который может быть использован в последующих вызовах функций, оперирующих с сокетами. В том же листинге показано описание функции 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() создан сокет, идентифицируемый дескриптором 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() (см.): она помещает его в структуру 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(), его следует пометить как готового принимать соединения ("слушающего").
#include <sys/socket.h>
int listen (int sd, int backlog);
Листинг 11.22. Описание функции listen().
Аргумент backlog сообщает операционной системе рекомендуемую длину очереди соединений, ожидающих обработки слушающим сокетом. Реализация должна поддерживать значения аргумента backlog вплоть до конфигурационной константы SOMAXCONN, определенной в заголовочном файле <sys/socket.h>. ОС имеет право установить длину очереди меньше рекомендуемой. При неположительном значении backlog очередь будет иметь зависящую от реализации минимальную длину.
Прием соединений выполняет функция accept(). Она выбирает первое соединение из очереди, ассоциированной с заданным дескриптором 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(). Аргументы 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. Описание функций семейства 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().
#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().

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика