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

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

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

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

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

//sockopt/checkopts.с
 1 #include "unp.h"
 2 #include <netinet/tcp.h> /* определения констант 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.

Оглавление книги


Генерация: 1.319. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз