UNIX: разработка сетевых приложений
Пример: сервер времени и даты в качестве демона
В листинге 13.2 представлено изменение нашего сервера времени и даты, не зависящего от протокола. В отличие от сервера, показанного в листинге 11.8, в нем вызывается функция
, чтобы этот сервер мог выполняться в качестве демона.daemon_initЛистинг 13.2. Не зависящий от протокола сервер времени и даты, работающий в качестве демона
//inetd/daytimetcpsrv2.c1 #include "unp.h"2 #include <time.h>3 int4 main(int argc, char **argv)5 {6 int listenfd, connfd;7 socklen_t addrlen, len;8 struct sockaddr *cliaddr;9 char buff[MAXLINE];10 time_t ticks;11 daemon_init(argv[0], 0);12 if (argc == 2)13 listenfd = Tcp_listen(NULL, argv[1], &addrlen);14 else if (argc == 3)15 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);16 else17 err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>");18 cliaddr = Malloc(addrlen);19 for (;;) {20 len = addrlen;21 connfd = Accept(listenfd, cliaddr, &len);22 err_msg("connection from %s", Sock_ntop(cliaddr, len));23 ticks = time(NULL);24 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));25 Write(connfd, buff, strlen(buff));26 Close(connfd);27 }28 }Изменений всего два: мы вызываем нашу функцию
, как только программа запускается, а затем вызываем нашу функциюdaemon_initвместоerr_msg, чтобы вывести IP-адрес и порт клиента. На самом деле, если мы хотим, чтобы наши программы могли выполняться как демоны, мы должны исключить вызов функцийprintfиprintfи вместо них использовать нашу функциюfprintf.err_msgОбратите внимание, что мы проверяем argc и выводим соответствующее сообщение до вызова
. Таким образом пользователь, запустивший демона, получает немедленное уведомление о недопустимом количестве аргументов. После вызоваdaemon_initвсе сообщения направляются в системный журнал.daemon_initЕсли мы запустим эту программу на нашем узле
и затем проверим файлlinux(куда мы отправляем все сообщения/var/log/messages) после соединения с тем же узлом, мы получим:LOG_USERJul 10 09:54:37 linux daytimetcpsrv2[24288]: connection from 127.0.0.1.55862Дата, время и имя узла автоматически ставятся в начале сообщения демоном
.syslogd13.5. Демон inetd
В типичной системе Unix может существовать много серверов, ожидающих запроса клиента. Примерами являются FTP, Telnet, Rlogin, TFTP и т.д. В системах, предшествующих 4.3BSD, каждая из этих служб имела связанный с ней процесс. Этот процесс запускался во время загрузки из файла
, и каждый процесс выполнял практически идентичные задачи запуска: создание сокета, связывание при помощи функции/etc/rcзаранее известного порта с сокетом, ожидание соединения (TCP) или получения дейтаграммы (UDP) и последующее выполнение функцииbind. Дочерний процесс выполнял обслуживание клиента, а родительский процесс ждал, когда поступит следующий запрос клиента. Эта модель характеризуется двумя недостатками.fork1. Все демоны содержали практически идентичный код запуска, направленный сначала на создание сокета, а затем на превращение процесса в процесс демона (аналогично нашей функции
).daemon_init2. Каждый демон занимал некоторое место в таблице процессов, но при этом большую часть времени находился в состоянии ожидания.
Реализация 4.3BSD упростила ситуацию, предоставив суперсервер (superserver) Интернета — демон
. Этот демон может применяться серверами, использующими TCP или UDP, и не поддерживает других протоколов, таких как доменные сокеты Unix. Демонinetdрешает две вышеупомянутые проблемы.inetd1. Он упрощает написание процессов демонов, поскольку обрабатывает большинство подробностей запуска. Таким образом устраняется необходимость вызова нашей функции
для каждого сервера.daemon_init2. Этот демон позволяет одиночному процессу (
) ждать входящие клиентские запросы ко множеству служб (вместо одного процесса для каждой службы). Это сокращает общее число процессов в системе.inetdПроцесс
сам становится демоном, используя технологии, которые мы изложили при описании функцииinetd. Затем он считывает и обрабатывает файл конфигурации, обычно файлdaemon_init. Этот файл задает, какие службы должен обрабатывать суперсервер, а также что нужно делать, когда приходит запрос к одной из этих служб. Каждая строка содержит поля, показанные в табл. 13.4. Вот несколько строк в качестве примера:/etc/inetd.confftp stream tcp nowait root /usr/bin/ftpd ftpd -ltelnet stream tcp nowait root /usr/bin/telnetd telnetdlogin stream tcp nowait root /usr/bin/rlogind rlogind -stftp dgram udp wait nobody /usr/bin/tftpd tftpd -s /tftpbootДействительное имя сервера всегда передается в качестве первого аргумента программе, выполняемой с помощью функции
.execТаблица 13.4. Поля файла inetd.conf
Поле Описание service-name Должен быть в /etc/services socket-type stream (TCP) или dgram (UDP) Protocol Должен быть в /etc/protocols; либо tcp, либо udp wait-flag Обычно nowait для TCP и wait для UDP login-name Из /etc/password; обычно root server-program Полное имя программы для вызова exec server-program-arguments Аргументы программы для вызова exec