Литмир - Электронная Библиотека
Содержание  
A
A
UNIX: разработка сетевых приложений - img_131.png

Рис. 25.1. Два варианта построения UDP-сервера

Большинство UDP-серверов (включая наш эхо-сервер, описанный в главе 8) построены так, как показано на рисунке слева. Однако NTP-сервер использует способ, показанный справа: когда прибывает новая дейтаграмма, она читается обработчиком сигнала

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

ПРИМЕЧАНИЕ

Вспомните листинг 22.3: процесс может установить параметр сокета IP_RECVDSTADDR, чтобы получить адрес получателя пришедшей UDP-дейтаграммы. Можно возразить, что вместе с полученной дейтаграммой UDP должны быть возвращены два дополнительных фрагмента информации — интерфейс, на котором была получена дейтаграмма (этот интерфейс может отличаться от адреса получателя, если узел использует более типичную модель системы с гибкой привязкой), и время прибытия дейтаграммы.

Для IPv6 интерфейс, на котором была получена дейтаграмма, можно получить, если включен параметр сокета IPV6_PKTINFO (см. раздел 22.8). Аналогичный параметр сокета IP_RECVIF для IPv4 описывался в разделе 22.2.

В FreeBSD также предусмотрен параметр сокета SO_TIMESTAMP, возвращающий время получения дейтаграммы как вспомогательные данные в структуре timeval. В Linux существует флаг SIOCGSTAMP для функции ioctl, которая возвращает структуру timeval, содержащую время прибытия дейтаграммы.

25.3. Эхо-сервер UDP с использованием сигнала SIGIO

В этом разделе мы приведем пример, аналогичный правой части рис. 25.1: UDP-сервер, использующий сигнал

SIGIO
для получения приходящих дейтаграмм. Этот пример также иллюстрирует использование надежных сигналов стандарта POSIX.

В данном случае клиент совсем не изменен по сравнению с листингами 8.3 и 8.4, а функция сервера main не изменилась по сравнению с листингом 8.1. Единственные внесенные изменения касаются функции

dg_echo
, которая будет приведена в следующих четырех листингах. В листинге 25.1[1] представлены глобальные объявления.

Листинг 25.1. Глобальные объявления

//sigio/dgecho01.c

 1 #include "unp.h"

 2 static int sockfd;

 3 #define QSIZE    8 /* размер входной очереди */

 4 #define MAXDG 4096 /* максимальный размер дейтаграммы */

 5 typedef struct {

 6  void *dg_data;          /* указатель на текущую дейтаграмму */

 7  size_t dg_len;          /* длина дейтаграммы */

 8  struct sockaddr *dg_sa; /* указатель на sockaddr{} с адресом клиента */

 9  socklen_t dg_salen;     /* длина sockaddr{} */

10 } DG;

11 static DG dg[QSIZE]; /* очередь дейтаграмм для обработки */

12 static long cntread[QSIZE +1]; /* диагностический счетчик */

13 static int iget; /* следующий элемент для обработки в основном цикле */

14 static int iput; /* следующий элемент для считывания обработчиком

                       сигналов */

15 static int nqueue; /* количество дейтаграмм в очереди на обработку

                         в основном цикле */

16 static socklen_t clilen; /* максимальная длина sockaddr{} */

17 static void sig_io(int);

18 static void sig_hup(int);

Очередь принимаемых дейтаграмм

3-12
 Обработчик сигнала
SIGIO
помещает приходящие дейтаграммы в очередь. Эта очередь является массивом структур
DG
, который интерпретируется как кольцевой буфер. Каждая структура содержит указатель на принятую дейтаграмму, ее длину и указатель на структуру адреса сокета, содержащую адрес протокола клиента и размер адреса протокола. В памяти размещается столько этих структур, сколько указано в
QSIZE
(в данном случае 8), и в листинге 25.2 будет видно, что функция
dg_echo
для размещения в памяти всех структур дейтаграмм и адресов сокетов вызывает функцию
malloc
. Также происходит выделение памяти под диагностический счетчик
cntread
, который будет рассмотрен чуть ниже. На рис. 25.2 приведен массив структур, при этом предполагается, что первый элемент указывает на 150-байтовую дейтаграмму, а длина связанного с ней адреса сокета равна 16.

UNIX: разработка сетевых приложений - img_132.png

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

Индексы массивов

13-15
 Переменная
iget
является индексом следующего элемента массива для обработки в основном цикле, а переменная
iput
— это индекс следующего элемента массива, в котором сохраняется результат действия обработчика сигнала. Переменная
nqueue
обозначает полное количество дейтаграмм, предназначенных для обработки в основном цикле.

В листинге 25.2 показан основной цикл сервера — функция

dg_echo
.

Листинг 25.2. Функция dg_echo: основной обрабатывающий цикл сервера

//sigio/dgecho01.c

19 void

20 dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg)

21 {

22  int i;

23  const int on = 1;

24  sigset_t zeromask, newmask, oldmask;

25  sockfd = sockfd_arg;

26  clilen = clilen_arg;

27  for (i = 0; i < QSIZE; i++) { /* инициализация очереди */

28   dg[i].dg_data = Malloc(MAXDG);

29   dg[i].dg_sa = Malloc(clilen);

30   dg[i].dg_salen = clilen;

31  }

32  iget = iput = nqueue = 0;

вернуться

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

277
{"b":"225366","o":1}