TCP-серверы
Для TCP-серверов inetd прослушивает хорошо известные порты, ожидая запроса на соединение, затем принимает соединение, ассоциирует с ним файловые Дескрипторы stdin, stdout и stderr, после чего запускает приложение. Таким образом, сервер может работать с соединением через дескрипторы 0, 1 и 2. Если это допускается конфигурационным файлом inetd (/etc/ inetd.conf), то inetd продолжает прослушивать тот же порт. Когда в этот порт поступает запрос на новое соединение, запускается новый экземпляр сервера, даже если первый еще не завершил сеанс. Это показано на рис. 3.2. Обратите внимание, что серверу не нужно обслуживать нескольких клиентов. Он просто выполняет запросы одного клиента, а потом завершается. Остальные клиенты обслуживаются дополнительными экземплярами сервера.
Рис. 3.2. Действия inetd при запуске TCP-сервера
Применение inetd освобождает от необходимости самостоятельно устанавливать TCP или UDP-соединение и позволяет писать сетевое приложение почти так же, как обычный фильтр. Простой, хотя и не очень интересный пример приведен в листинге 3.3.
Листинг 3.3. Программа rlnumd для подсчета строк
1 #include <stdio.h>
2 void main( void )
3 {
4 int cnt = 0;
5 char line[ 1024 ];
6 /*
7 *Мы должны явно установить режим построчной буферизации,
8 *так как функции из библиотеки стандартного ввода/вывод
9 *не считают сокет терминалом. */
10 setvbuf( stdout, NULL, _IOLBF, 0 );
11 while ( fgets ( line, sizeof( line ) , stdin ) != NULL )
12 printf( "%3i: %s", ++cnt, line );
13 }
По поводу этой программы стоит сделать несколько замечаний:
Примечание: Этот факт, который указан в книге [Oliver 2000], служит еще одним примером того, как можно легко допустить ошибку переполнения буфера. Подробнее этот вопрос обсуждался в совете 11.
Программа в листинге 3.3 может работать и как «нормальный» фильтр, и как Удаленный сервис подсчета строк. Чтобы превратить ее в удаленный сервис, нужно только выбрать номер порта, добавить в файл /etc/ services строку с именем сервиса и номером порта и включить в файл /etc/inetd.conf строку, описывающую этот сервис и путь к исполняемой программе. Например, если вы назовете сервис rlnum, исполняемую программу для него –
rlnumd и назначите ему порт 8000, то надо будет добавить в /etc/services строку
rlnum 8000/tcp # удаленный сервис подсчета строк,
а в /etc/inetd.conf - строку
rlnum stream tcp nowait jcs /usr/home/jcs/rlnumd rlnumd.
Добавленная в /etc/services строка означает, что сервис rlnum использует протокол TCP по порту 8000. Смысл же полей в строке, добавленной в /etc/inetd.conf, таков:
- в качестве механизма рестарта для ненадежных сетевых программ-демонов. Пока демон работает корректно, он принимает соединения от клиентов, но если по какой-то причине демон «падает», то при следующей попытке соединения inetd его рестартует;
- как способ гарантировать одновременное подключение только одного клиента;
- как способ управления многопоточным или многопроцессным приложением, зависящим от нагрузки. В этом случае начальный процесс запускается inetd, а затем он динамически балансирует нагрузку, создавая по мере необходимости дополнительные процессы или потоки. При уменьшении нагрузки, потоки уничтожаются, а в случае длительного простоя завершает работу и сам процесс, освобождая ресурсы и возвращая прослушивающий сокет inetd.
В данном примере задан флаг nowait, как и обычно для TCP-серверов.
- имя пользователя, с правами которого будет запущен сервер. Это имя должно присутствовать в файле /etc/passwd. Большинство стандартных серверов, прописанных в inetd. conf, запускаются от имени root, но это совершенно необязательно. Здесь в качестве имени пользователя выбрано jcs;
- полный путь к файлу исполняемой программы. Поскольку rlnumd находится в каталоге пользователя jcs, задан путь /usr/home/ jcs/rlnumd;
- до пяти аргументов (начиная с argv [ 0 ]), которые будут переданы серверу. Поскольку в этом примере у сервера нет аргументов, оставлен только argv [ 0 ]
Чтобы протестировать сервер, необходимо заставить inetd перечитать свой конфигурационный файл (в большинстве реализаций для этого нужно послать ему сигнал SIGHUP) и соединиться с помощью telnet:
bsd: $ telnet localhost rlnum
Trying 127.0.0.1. . .
Connected to localhost
Escape character is "^]".
hello
1: hello
world
2: world
^]
telnet> quit
Connection closed.
bsd: $