UNIX: разработка сетевых приложений
UNIX: разработка сетевых приложений читать книгу онлайн
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Листинг 30.5. Функция web_child: обработка каждого клиентского запроса
//server/web_child.c
1 #include "unp.h"
2 #define MAXN 16384 /* максимальное количество байтов, которое клиент
может запросить */
3 void
4 web_child(int sockfd)
5 {
6 int ntowrite;
7 ssize_t nread;
8 char line[MAXLINE], result[MAXN];
9 for (;;) {
10 if ((nread = Readline(sockfd, line, MAXLINE)) == 0)
11 return; /* соединение закрыто другим концом */
12 /* line задает, сколько байтов следует отправлять обратно */
13 ntowrite = atol(line);
14 if ((ntowrite <= 0) || (ntowrite > MAXN))
15 err_quit("client request for bytes", ntowrite);
16 Writen(sockfd, result, ntowrite);
17 }
18 }
Установив соединение с сервером, клиент записывает одну строку, задающую количество байтов, которое сервер должен вернуть. Это отчасти похоже на HTTP: клиент отправляет небольшой запрос, а сервер в ответ отправляет требуемую информацию (часто это файл HTML или изображение GIF). В случае HTTP сервер обычно закрывает соединение после отправки клиенту затребованных данных, хотя более новые версии используют постоянные соединения (persistent connection), оставляя соединения TCP открытыми для дополнительных клиентских запросов. В нашей функции
web_child
В строке 1 табл. 30.1 показаны результаты измерения времени, затраченного параллельным сервером. При сравнении со следующими строками этой таблицы видно, что параллельный сервер тратит больше процессорного времени, чем все другие типы серверов — то, что мы и ожидали при вызове функции
fork
Один из способов устройства сервера, который мы не рассматриваем в этой главе, — это сервер, инициируемый демоном inetd (см. раздел 13.5). С точки зрения управления процессами такой сервер подразумевает использование функций fork и exec, так что затраты времени центрального процессора будут еще больше, чем показанные в строке 1 для параллельного сервера.
30.6. Сервер TCP с предварительным порождением процессов без блокировки для вызова accept
В первом из рассматриваемых нами «усовершенствованных» серверов используется технология, называемая предварительным созданием процессов (preforking). Вместо того чтобы вызывать функцию
fork
Рис. 30.1. Предварительное создание дочерних процессов сервером
Преимущество этой технологии заключается в том, что обслуживание нового клиента не требует вызова функции
fork
backlog
listen
accept
connect
За счет некоторого дополнительного усложнения кода всегда можно добиться того, что сервер справится со всеми клиентскими запросами. От родительского процесса требуется постоянно отслеживать количество свободных дочерних процессов, и если это количество падает ниже некоторого минимального предела, родитель должен вызвать функцию
fork
Но прежде чем углубляться в детали, исследуем основную структуру этого типа сервера. В листинге 30.6 показана функция
main
Листинг 30.6. Функция main сервера с предварительным порождением дочерних процессов
//server/serv02.c
1 #include "unp.h"
2 static int nchildren;
3 static pid_t *pids;
4 int
5 main(int argc, char **argv)
6 {
7 int listenfd, i;
8 socklen_t addrlen;
9 void sig_int(int);
10 pid_t child_make(int, int, int);
11 if (argc == 3)
12 listenfd = Tcp_listen(NULL, argv[1], &addrlen);
13 else if (argc == 4)
14 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
15 else