355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Уильям Ричард Стивенс » UNIX: разработка сетевых приложений » Текст книги (страница 19)
UNIX: разработка сетевых приложений
  • Текст добавлен: 17 сентября 2016, 20:42

Текст книги "UNIX: разработка сетевых приложений"


Автор книги: Уильям Ричард Стивенс


Соавторы: Эндрю М. Рудофф,Билл Феннер

Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 19 (всего у книги 88 страниц) [доступный отрывок для чтения: 32 страниц]

6.11. Эхо-сервер TCP (еще раз)

Теперь мы изменим наш эхо-сервер TCP из раздела 6.8, используя вместо функции selectфункцию poll. В предыдущей версии сервера, работая с функцией select, мы должны были выделять массив clientвместе с набором дескрипторов rset(см. рис. 6.12). С помощью функции pollмы разместим в памяти массив структур pollfd. В нем же мы будем хранить и информацию о клиенте, не создавая для нее другой массив. Элемент fdэтого массива мы обрабатываем тем же способом, которым обрабатывали массив client(см. рис. 6.12): значение -1 говорит о том, что элемент не используется, а любое другое значение является номером дескриптора. Вспомните из предыдущего раздела, что любой элемент в массиве структур pollfd, передаваемый функции pollс отрицательным значением элемента fd, просто игнорируется.

В листинге 6.5 показана первая часть кода нашего сервера.

Листинг 6.5. Первая часть сервера TCP, использующего функцию poll

//tcpcliserv/tcpservpoll01.с

 1 #include "unp.h"

 2 #include <1imits.h> /* для OPEN_MAX */

 3 int

 4 main(int argc, char **argv)

 5 {

 6  int i, maxi, listenfd, connfd, sockfd;

 7  int nready;

 8  ssize_t n;

 9  char buf[MAXLINE];

10  socklen_t clilen;

11  struct pollfd client[OPEN_MAX];

12  struct sockaddr_in cliaddr, servaddr;

13  listenfd = Socket(AF_INET, SOCK_STREAM, 0);

14  bzero(&servaddr, sizeof(servaddr));

15  servaddr.sin_family = AF_INET;

16  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

17  servaddr.sin_port = htons(SERV_PORT);

18  Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));

19  Listen(listenfd, LISTENQ);

20  client[0].fd = listenfd;

21  client[0].events = POLLRDNORM;

22  for (i = 1; i < OPEN_MAX; i++)

23   client[i].fd = -1; /* -1 означает, что элемент свободен */

24  maxi = 0; /* максимальный индекс массива client[] */

Размещение массива структур pollfd в памяти

11 Мы объявляем массив структур pollfdразмером OPEN_MAX. Не существует простого способа определить максимальное число дескрипторов, которые могут быть открыты процессом. Мы снова столкнемся с этой проблемой в листинге 13.1. Один из способов ее решения – вызвать функцию POSIX sysconfс аргументом _SC_OPEN_MAX[110, с. 42-44], а затем динамически выделять в памяти место для массива соответствующего размера. Однако функция sysconfможет возвратить некое «неопределенное» значение, и в этом случае нам придется задавать ограничение самим. Здесь мы используем только константу OPEN_MAXстандарта POSIX.

Инициализация

20-24 Мы используем первый элемент в массиве clientдля прослушиваемого сокета и присваиваем дескрипторам для оставшихся элементов -1. Мы также задаем в качестве аргумента функции pollсобытие POLLRDNORM, чтобы получить уведомление от этой функции в том случае, когда новое соединение будет готово к приему. Переменная maxiсодержит максимальный индекс массива client, используемый в настоящий момент.

Вторая часть нашей функции приведена в листинге 6.6.

Листинг 6.6. Вторая часть сервера TCP, использующего функцию poll

//tcpcliserv/tcpservpoll01.c

25  for (;;) {

26   nready = Poll(client, maxi + 1, INFTIM);

27   if (client[0].revents & POLLRDNORM) { /* новое соединение

                                              с клиентом */

28    clilen = sizeof(cliaddr);

29    connfd = Accept(listenfd. (SA*)&cliaddr, &clilen);

30    for (i = 1; i < OPEN_MAX; i++)

31     if (client[1].fd < 0) {

32      client[i].fd = connfd; /* сохраняем дескриптор */

33      break;

34     }

35    if (i == OPEN_MAX)

36     err_quit("too many clients");

37    client[i].events = POLLRDNORM;

38    if (i > maxi)

39     maxi = i; /* максимальный индекс в массиве client[] */

40    if (–nready <= 0)

41     continue; /* больше нет дескрипторов, готовых для чтения */

42   }

43   for (i = 1; i <= maxi; i++) { /* проверяем все клиенты на наличие

                                      данных */

44    if ((sockfd = client[i].fd) < 0)

45     continue;

46    if (client[i].revents & (POLLRDNORM | POLLERR)) {

47     if ((n = Read(sockfd, buf, MAXLINE)) < 0) {

48      if (errno == ECONNRESET) {

49       /* соединение переустановлено клиентом */

50       Close(sockfd);

51       client[i].fd = -1;

52      } else

53       err_sys("readline error");

54     } else if (n == 0) {

55      /* соединение закрыто клиентом */

56      Close(sockfd);

57      client[i].fd = -1;

58     } else

59      Writen(sockfd, line, n);

60     if (–nready <= 0)

61      break; /* больше нет дескрипторов, готовых для чтения */

62    }

63   }

64  }

65 }

Вызов функции poll, проверка нового соединения

26-42 Мы вызываем функцию pollдля ожидания нового соединения либо данных на существующем соединении. Когда новое соединение принято, мы находим первый свободный элемент в массиве client – это первый элемент с отрицательным дескриптором. Обратите внимание, что мы начинаем поиск с индекса 1, поскольку элемент client[0]используется для прослушиваемого сокета. Когда свободный элемент найден, мы сохраняем дескриптор и устанавливаем событие POLLRDNORM.

Проверка данных на существующем соединении

43-63 Два события, которые нас интересуют, – это POLLRDNORMи POLLERR. Второй флаг в элементе eventмы не устанавливали, поскольку этот флаг возвращается всегда, если соответствующее условие выполнено. Причина, по которой мы проверяем событие POLLERR, в том, что некоторые реализации возвращают это событие, когда приходит сегмент RST, другие же в такой ситуации возвращают событие POLLRDNORM. В любом случае мы вызываем функцию read, и если произошла ошибка, эта функция возвратит ее. Когда существующее соединение завершается клиентом, мы просто присваиваем элементу fdзначение -1.

6.12. Резюме

В Unix существует пять различных моделей ввода-вывода:

■ блокируемый ввод-вывод;

■ неблокируемый ввод-вывод;

■ мультиплексирование ввода-вывода;

■ управляемый сигналом ввод-вывод;

■ асинхронный ввод-вывод.

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

Наиболее часто используемой функцией для мультиплексирования ввода– вывода является функция select. Мы сообщаем этой функции, какие дескрипторы нас интересуют (для чтения, записи или условия ошибки), а также передаем ей максимальное время ожидания и максимальное число дескрипторов (увеличенное на единицу). Большинство вызовов функции selectопределяют количество дескрипторов, готовых для чтения, и, как мы отметили, единственное условие исключения при работе с сокетами – это прибытие внеполосных данных (см. главу 21). Поскольку функция selectпозволяет ограничить время блокирования функции, мы используем это свойство в листинге 14.3 для ограничения по времени операции ввода.

Используя эхо-клиент в пакетном режиме с помощью функции select, мы выяснили, что даже если обнаружен признак конца файла, данные все еще могут находиться в канале на пути к серверу или от сервера. Обработка этого сценария требует применения функции shutdown, которая позволяет воспользоваться таким свойством TCP, как возможность половинного закрытия соединения (half-close feature).

POSIX определяет функцию pselect(повышающую точность таймера с микросекунд до наносекунд) которой передается новый аргумент – указатель на набор сигналов. Это позволяет избежать ситуации гонок (race condition) при перехвате сигналов, о которой мы поговорим более подробно в разделе 20.5.

Функция pollиз System V предоставляет функциональность, аналогичную функции select. Кроме того, она обеспечивает дополнительную информацию при работе с потоковыми устройствами. POSIX требует наличия и функции select, и функции poll, но первая распространена шире.

Упражнения

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

2. Описывая в разделе 6.3 условия, при которых функция selectсообщает, что дескриптор готов для записи, мы указали, что сокет должен быть неблокируемым, для того чтобы операция записи возвратила положительное значение. Почему?

3. Что произойдет с программой из листинга 6.1, если мы поставим слово elseперед ifв строке 19?

4. В листинге 6.3 добавьте необходимый код, чтобы позволить серверу использовать максимальное число дескрипторов, допустимое ядром ( Подсказка: изучите функцию setrlimit.)

5. Посмотрите, что происходит, если в качестве второго аргумента функции shutdownпередается SHUT_RD. Возьмите за основу код клиента TCP, представленный в листинге 5.3, и выполните следующие изменения: вместо номера порта SERV_PORTзадайте порт 19 (служба chargen, см. табл. 2.1), а также замените вызов функции str_cliвызовом функции pause. Запустите программу, задав IP-адрес локального узла, на котором выполняется сервер chargen. Просмотрите пакеты с помощью такой программы, как, например, tcpdump(см. раздел В.5). Что происходит?

6. Почему приложение должно вызывать функцию shutdownс аргументом SHUT_RDWR, вместо того чтобы просто вызвать функцию close?

7. Что происходит в листинге 6.4, когда клиент отправляет RST для завершения соединения?

8. Перепишите код, показанный в листинге 6.5, чтобы вызывать функцию sysconfдля определения максимального числа дескрипторов и размещения соответствующего массива clientв памяти.

Глава 7
Параметры сокетов
7.1. Введение

Существуют различные способы получения и установки параметров сокетов:

■ функции getsockoptи setsockopt;

■ функция fcntl;

■ функция ioctl.

Эту главу мы начнем с описания функций getsockoptи setsockopt. Далее мы приведем пример, в котором выводятся заданные по умолчанию значения параметров, а затем дадим подробное описание всех параметров сокетов. Мы разделили описание параметров на следующие категории: общие, IPv4, IPv6, TCP и SCTP. При первом прочтении главы можно пропустить подробное описание параметров и при необходимости прочесть отдельные разделы, на которые даны ссылки. Отдельные параметры подробно описываются в дальнейших главах, например параметры многоадресной передачи IPv4 и IPv6 мы обсуждаем в разделе 19.5.

Мы также рассмотрим функцию fcntl, поскольку она реализует предусмотренные стандартом POSIX возможности отключить для сокета блокировку ввода-вывода, включить управление сигналами, а также установить владельца сокета. Функцию ioctlмы опишем в главе 17.

7.2. Функции getsockopt и setsockopt

Эти две функции применяются только к сокетам.

#include

int getsockopt(int sockfd, int level, int optname, void * optval, socklen_t * optlen);

int setsockopt(int sockfd, int level, int optname, const void * optval, socklen_t optlen);

Обе функции возвращают 0 в случае успешного завершения, -1 в случае ошибки

Переменная sockfdдолжна ссылаться на открытый дескриптор сокета. Переменная levelопределяет, каким кодом должен интерпретироваться параметр: общими программами обработки сокетов или зависящими от протокола программами (например, IPv4, IPv6, TCP или SCTP).

optval– это указатель на переменную, из которой извлекается новое значение параметра с помощью функции setsockoptили в которой сохраняется текущее значение параметра с помощью функции getsockopt. Размер этой переменной задается последним аргументом. Для функции setsockoptтип этого аргумента – значение, а для функции getsockopt– « значение-результат».

В табл. 7.1 и 7.2 сведены параметры, которые могут запрашиваться функцией getsockoptили устанавливаться функцией setsockopt. В колонке «Тип данных» приводится тип данных того, на что указывает указатель optvalдля каждого параметра. Две фигурные скобки мы используем, чтобы обозначить структуру, например linger{}обозначает struct linger.

Таблица 7.1. Параметры сокетов для функций getsockopt и setsockopt


SOL_SOCKETSO_BROADCASTПозволяет посылать широковещательные дейтаграммыint
SO_DEBUGРазрешает отладкуint
SO_DONTROUTEОбходит таблицу маршрутизацииint
SO_ERRORПолучает ошибку, ожидающую обработки, и возвращает значение параметра в исходное состояниеint
SO_KEEPALIVEПериодически проверяет, находится ли соединение в рабочем состоянииint
SO_LINGERЗадерживает закрытие сокета, если имеются данные для отправкиlinger{}
SO_OOBINLINEОставляет полученные внеполосные данные вместе с обычными данными (inline)int
SO_RCVBUFРазмер приемного буфераint
SO_SNDBUFРазмер буфера отправкиint
SO_RCVLOWATМинимальное количество данных для приемного буфера сокетаint
SO_SNDLOWATМинимальное количество данных для буфера отправки сокетаint
SO_RCVTIMEOТайм-аут при полученииtimeval{}
SO_SNDTIMEOТайм-аут при отправкеtimeval{}
SO_REUSEADDRДопускает повторное использование локального адресаint
SO_REUSEPORTДопускает повторное использование локального адресаint
SO_TYPEВозвращает тип сокетаint
SO_USELOOPBACKМаршрутизирующий сокет получает копию того, что он отправляетint
IPPROTO_IPIP_HDRINCLВключается IP– заголовокint
IP_OPTIONSВ заголовке IPv4 устанавливаются параметры IPсм. текст
IP_RECVDSTADDRВозвращает IP-адрес получателяint
IP_RECVIFВозвращает индекс интерфейса, на котором принимается дейтаграмма UDPint
IP_TOSТип сервиса и приоритетint
IP_TTLВремя жизниint
IP_MULTICAST_IFЗадает интерфейс для исходящих дейтаграммin_addr{}
IP_MULTICAST_TTLЗадает TTL для исходящих дейтаграммu_char
IP_MULTICAST_LOOPРазрешает или отменяет отправку копии дейтаграммы на тот узел, откуда она была послана (loopback)u_char
IP_ADD_MEMBERSHIPВключение в группу многоадресной передачиip_mreq{}
IP_DROP_MEMBERSHIPОтключение от группы многоадресной передачиip_mreq{}
IP_{BLOCK, UNBLOCK}_SOURCEБлокирование и разблокирование источника многоадресной передачиip_mreq_source{}
IP_{ADD, DROP}_SOURCE_MEMBERSHIPПрисоединение или отключение от многоадресной передачи от источника (source-specific)ip_mreq_source{}
IPPROTO_ICMPV6ICMP6_FILTERУказывает тип сообщения ICMPv6, которое передается процессуicmp6_filter{}
IPPROTO_IPV6IPV6_ADDRFORMМеняет формат адреса сокетаint
IPV6_CHECKSUMОтступ поля контрольной суммы для символьных (неструктурированных) сокетовint
IPV6_DONTFRAGНе фрагментировать, а сбрасывать большие пакетыint
IPV6_NEXTHOPЗадает следующий транзитный адресsockaddr{}
IPV6_PATHMTUПолучение текущей маршрутной МТУip6_mtuinfo{}
IPV6_RECVDSTOPTSПолучение параметров адресатаint
IPV6_RECVHOPLIMITПолучение ограничения на количество транзитных узлов при направленной передачеint
IPV6_RECVHOPOPTSПолучение параметров прыжковint
IPV6_RECVPATHMTUПолучение маршрутной MTUint
IPV6_RECVPKTINFOПолучение информации о пакетахint
IPV6_RECVRTHDRПолучение маршрута от источникаint
IPV6_RECVTCLASSПолучение класса трафикаint
IPV6_UNICAST_HOPSПредел количества транзитных узлов, задаваемый по умолчаниюint
IPV6_USE_MIN_MTUИспользовать минимальную MTUint
IPV6_V60NLYОтключить совместимость с IPv4int
IPV6_XXXВспомогательные данныесм. текст
IPV6_MULTICAST_IFЗадает интерфейс для исходящих дейтаграммu_int
IPV6_MULTICAST_HOPSЗадает предельное количество транзитных узлов для исходящих широковещательных сообщенийint
IPV6_MULTICAST_LOOPРазрешает или отменяет отправку копии дейтаграммы на тот узел, откуда она была послана (loopback)u_int
IPV6_LEAVE_GROUPВыход из группы многоадресной передачиipv6_mreq{}
IPPROTO_IP или IPPROTO_IPV6MCAST_JOIN_GROUPПрисоединение к группе многоадресной передачиgroup_req{}
MCAST_LEAVE_GROUPВыход из группы многоадресной передачиgroup_source_req{}
MCAST_BLOCK_SOURCEБлокирование источника многоадресной передачиgroup_source_req{}
MCAST_UNBLOCK_SOURCEРазблокирование источника многоадресной передачиgroup_source_req{}
MCAST_JOIN_SOURCE_GROUPПрисоединение к группе многоадресной передачи от источникаgroup_source_req{}
MCAST_LEAVE_SOURCE_GROUPВыход из группы многоадресной передачи от источникаgroup_source_req{}

Таблица 7.2. Параметры сокетов транспортного уровня


IPPROTO_TCPTCP_MAXSEGМаксимальный размер сегмента TCPint
TCP_NODELAYОтключает алгоритм Наглаint
IPPROTO_SCTPSCTP_ADAPTION_LAYERУказание на уровень адаптацииsctp_setadaption
SCTP_ASSOCINFO+Получение и задание сведений об ассоциацииsctp_assocparamms{}
SCTP_AUTOCLOSEАвтоматическое закрытиеint
SCTP_DEFAULT_SEND_PARAMПараметры отправки но умолчаниюsctp_sndrcvinfo{}
SCTP_DISABLE_FRAGMENTSФрагментация SCTPint
SCTP_EVENTSУведомление об интересующих событияхsctp_event_subscribe{}
SCTP_GET_PEER_ADDR_INFO+Получение состояния адреса собеседникаsctp_paddrinfo{}
SCTP_I_WANT_MAPPED_V4_ADDRОтображение адресов IPv4int
SCTP_INITMSGПараметры пакета INIT по умолчаниюsctp_initmsg{}
SCTP_MAXBURSTМаксимальный размер набора пакетовint
SCTP_MAXSEGМаксимальный размер фрагментацииint
SCTP_NODELAYОтключение алгоритма Наглаint
SCTP_PEER_ADDR_PARAMS+Параметры адреса собеседникаsctp_paddrparams{)
SCTP_PRIMARY_ADDR+Основной адрес назначенияsctp_setprim{}
SCTP_RTOINFO+Информация RTOsctp_rtoinfo{}
SCTP_SET_PEER_PRIMARY_ADDRОсновной адрес назначения собеседникаsctp_setpeerprim{}
SCTP_STATUS+Получение сведений о статусе ассоциацииsctp_status{}

Существует два основных типа параметров: двоичные параметры, включающие или отключающие определенное свойство (флаги), и параметры, получающие и возвращающие значения параметров, которые мы можем либо задавать, либо проверять. В колонке «Флаг» указывается, относится ли параметр к флагам. Для флагов при вызове функции getsockoptаргумент *optvalявляется целым числом. Возвращаемое значение *optvalнулевое, если параметр отключен, и ненулевое, если параметр включен. Аналогично, функция setsockoptтребует ненулевого значения *optvalдля включения параметра, и нулевого значения – для его выключения. Если в колонке «Флаг» не содержится символа «•», то параметр используется для передачи значения заданного типа между пользовательским процессом и системой.

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

7.3. Проверка наличия параметра и получение значения по умолчанию

Напишем программу, которая проверяет, поддерживается ли большинство параметров, представленных в табл. 7.1 и 7.2, и если да, то выводит их значения, заданные по умолчанию. В листинге 7.1 [1]1
  Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.


[Закрыть]
содержатся объявления нашей программы.

Листинг 7.1. Объявления для нашей программы, проверяющей параметры сокетов

//sockopt/checkopts.с

 1 #include "unp.h"

 2 #include /* определения констант TCP_xxx */

 3 union val {

 4  int i_val;

 5  long l_val;

 6  struct linger linger_val;

 7  struct timeval timeval_val;

 8 } val;

 9 static char *sock_str_flag(union val*, int);

10 static char *sock_str_int(union val*, int);

11 static char *sock_str_linger(union val*, int);

12 static char *sock_str_timeval(union val*, int);

13 struct sock_opts {

14  const char *opt_str;

15  int opt_level;

16  int opt_name;

17  char *(*opt_val_str)(union val*, int);

18 } sock_opts[] = {

19  { "SO_BROADCAST",      SOL_SOCKET,   SO_BROADCAST,   sock_str_flag },

20  { "SO_DEBUG",          SOL_SOCKET,   SO_DEBUG,       sock_str_flag },

21  { "SO_DONTROUTE",      SOL_SOCKET,   SO_DONTROUTE,   sock_str_flag },

22  { "SO_ERROR",          SOL_SOCKET,   SO_ERROR,       sock_str_int },

23  { "SO_KEEPALIVE",      SOL_SOCKET,   SO_KEEPALIVE,   sock_str_flag },

24  { "SO_LINGER",         SOL_SOCKET,   SO_LINGER,      sock_str_linger },

25  { "SO_OOBINLINE",      SOL_SOCKET,   SO_OOBINLINE,   sock_str_flag },

26  { "SO_RCVBUF",         SOL_SOCKET,   SO_RCVBUF,      sock_str_int },

27  { "SO_SNDBUF",         SOL_SOCKET,   SO_SNDBUF,      sock_str_int },

28  { "SO_RCVLOWAT",       SOL_SOCKET,   SO_RCVLOWAT,    sock_str_int },

29  { "SO_SNDLOWAT",       SOL_SOCKET,   SO_SNDLOWAT,    sock_str_int },

30  { "SO_RCVTIMEO",       SOL_SOCKET,   SO_RCVTIMEO,    sock_str_timeval },

31  { "SO_SNDTIMEO",       SOL_SOCKET,   SO_SNDTIMEO,    sock_str_timeval },

32  { "SO_REUSEADDR",      SOL_SOCKET,   SO_REUSEADDR,   sock_str_flag },

33 #ifdef SO_REUSEPORT

34  { "SO_REUSEPORT",      SOL_SOCKET,   SO_REUSEPORT,   sock_str_flag },

35 #else

36  { "SO_REUSEPORT",      0,            0, NULL },

37 #endif

38  { "SO_TYPE",           SOL_SOCKET,   SO_TYPE,        sock_str_int },

39  { "SO_USELOOPBACK",    SOL_SOCKET,   SO_USELOOPBACK, sock_str_flag },

40  { "IP_TOS",            IPPROTO_IP,   IP_TOS,         sock_str_int },

41  { "IP_TTL",            IPPROTO_IP,   IP_TTL,         sock_str_int },

42  { "IPV6_DONTFRAG",     IPPROTO_IPV6, IPV6_DONTFRAG,  sock_str_flag },

43  { "IPV6_UNICAST_HOPS", IPPROTO_IPV6, IPV6_UNICAST_HOPS, sock_str_int },

44  { "IPV6_V6ONLY",       IPPROTO_IPV6, IPV6_V6ONLY,    sock_str_flag },

45  { "TCP_MAXSEG",        IPPROTO_TCP,  TCP_MAXSEG,     sock_str_int },

46  { "TCP_NODELAY",       IPPROTO_TCP,  TCP_NODELAY,    sock_str_flag },

47  { "SCTP_AUTOCLOSE",    IPPROTO_SCTP, SCTP_AUTOCLOSE, sock_str_int },

48  { "SCTP_MAXBURST",     IPPROTO_SCTP, SCTP_MAXBURST,  sock_str_int },

49  { "SCTP_MAXSEG",       IPPROTO_SCTP, SCTP_MAXSEG,    sock_str_int },

50  { "SCTP_NODELAY",      IPPROTO_SCTP, SCTP_NODELAY,   sock_str_flag },

51  { NULL,                0,            0,              NULL }

52 };

Объявление объединения возможных значений

3-9 Наше объединение valсодержит по одному элементу для каждого возможного возвращаемого значения из функции getsockopt.

Задание прототипов функций

10-13 Мы определяем прототипы для четырех функций, которые вызываются для вывода значения данного параметра сокета.

Задание структуры и инициализация массива

14-46 Наша структура sock_optsсодержит всю информацию, которая необходима, чтобы вызвать функцию getsockoptдля каждого из параметров сокета и вывести его текущее значение. Последний элемент, opt_val_str, является указателем на одну из четырех функций, которые выводят значение параметра. Мы размещаем в памяти и инициализируем массив этих структур, каждый элемент которого соответствует одному параметру сокета.

ПРИМЕЧАНИЕ

Не все реализации поддерживают полный набор параметров сокетов. Чтобы определить, поддерживается ли данный параметр, следует использовать #ifdef или #if defined, как показано для параметра SO_REUSEPORT. Для полноты картины требуется обработать подобным образом все параметры, но в книге мы пренебрегаем этим, потому что #ifdef только удлиняет показанный код и не влияет на суть дела.

В листинге 7.2 показана наша функция main.

Листинг 7.2. Функция main для проверки параметров сокетов

//sockopt/checkopts.c

53 int

54 main(int argc, char **argv)

55 {

56  int fd;

57  socklen_t len;

58  struct sock_opts *ptr;

59  for (ptr = sock_opts; ptr->opt_str != NULL; ptr++) {

60   printf("%s: ptr->opt_str);

61   if (ptr->opt_val_str == NULL)

62    printf("(undefined)n");

63   else {

64    switch(ptr->opt_level) {

65    case SOL_SOCKET:

66    case IPPROTO_IP:

67    case IPPROTO_TCP:

68     fd = Socket(AF_INET, SOCK_STREAM, 0);

69     break;

70 #ifdef IPV6

71    case IPPROTO_IPV6:

72     fd = Socket(AF_INET6, SOCK_STREAM, 0);

73     break;

74 #endif

75 #ifdef IPPROTO_SCTP

76    case IPPROTO_SCTP:

77     fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);

78     break;

79 #endif

80    default:

81     err_quit("Can't create fd for level %dn", ptr->opt_level);

82    }

83    len = sizeof(val);

84    if (getsockopt(fd, ptr->opt_level, ptr->opt_name,

85     &val, &len) == -1) {

86     err_ret("getsockopt error");

87    } else {

88     printf("default = %sn", (*ptr->opt_val_str)(&val, len));

89    }

90    close(fd);

91   }

92  }

93  exit(0);

94 }

Перебор всех параметров

59-63 Мы перебираем все элементы нашего массива. Если указатель opt_val_strпустой, то параметр не определен реализацией (что, как мы показали, возможно для SO_REUSEPORT).

Создание сокета

63-82 Мы создаем сокет, на котором проверяем действие параметров. Для проверки параметров сокета и уровней IPv4 и TCP мы используем сокет IPv4 TCP. Для проверки параметров сокетов уровня IPv6 мы используем сокет IPv6 TCP, а для проверки параметров SCTP – сокет IPv4 SCTP.

Вызов функции getsockopt

83-87 Мы вызываем функцию getsockopt, но не завершаем ее выполнение, если возвращается ошибка. Многие реализации определяют имена некоторых параметров сокетов, даже если не поддерживают эти параметры. Неподдерживаемые параметры выдают ошибку ENOPROTOOPT.

Вывод значения параметра по умолчанию

88-89 Если функция getsockoptуспешно завершается, мы вызываем нашу функцию для преобразования значения параметра в строку и выводим эту строку.

В листинге 7.1 мы показали четыре прототипа функций, по одному для каждого типа возвращаемого значения параметра. В листинге 7.3 показана одна из этих функций, sock_str_flag, которая выводит значение параметра, являющегося флагом. Другие три функции аналогичны этой.

Листинг 7.3. Функция sock_str_flag: преобразование флага в строку

//sockopt/checkopts.с

 95 static char strres[128];

 96 static char *

 97 sock_str_flag(union val *ptr, int len)

 98 {

 99  if (len != sizeof(int))

100   snprint(strres, sizeof(strres), "size (%d) not sizeof(int)", len);

101  else

102   snprintf(strres, sizeof(strres),

103    "%s", (ptr->i_val == 0) ? "off" : "on");

104  return(strres);

105 }

99-104 Вспомните, что последний аргумент функции getsockopt– это аргумент типа «значение-результат». Первое, что мы проверяем, – это то, что размер значения, возвращаемого функцией getsockopt, совпадает с предполагаемым. В зависимости от того, является ли значение флага нулевым или нет, возвращается строка offили on.

Выполнение этой программы под FreeBSD 4.8 с пакетами обновлений KAME SCTP дает следующий вывод:

freebsd % checkopts

SO_BROADCAST: default = off

SO_DEBUG: default = off

SO_DONTROUTE: default = off

SO_ERROR: default = 0

SO_KEEPALIVE: default = off

SO_LINGER: default = l_onoff = 0, l_linger = 0

SO_OOBINLINE: default = off

SO_RCVBUF: default = 57344

SO_SNDBUF: default = 32768

SO_RCVLOWAT: default = 1

SO_SNDLOWAT: default = 2048

SO_RCVTIMEO: default = 0 sec, 0 usec

SO_SNDTIMEO: default = 0 sec, 0 usec

SO_REUSEADDR: default = off

SO_REUSEPORT: default = off

SO_TYPE: default = 1

SO_USELOOPBACK: default = off

IP_TOS: default = 0

IP_TTL: default = 64

IPV6_DONTFRAG: default = off

IPV6_UNICAST_HOPS: default = -1

IPV6_V6ONLY: default = off

TCP_MAXSEG: default = 512

TCP_NODELAY: default = off

SCTP_AUTOCLOSE: default = 0

SCTP_MAXBURST: default = 4

SCTP_MAXSEG: default = 1408

SCTP_NODELAY: default = off

Значение 1, возвращаемое для параметра SO_TYPE, для этой реализации соответствует SOCK_STREAM.


    Ваша оценка произведения:

Популярные книги за неделю