Литмир - Электронная Библиотека
Содержание  
A
A

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

libnet
скрывает различия между системами и предоставляет интерфейс для вывода через символьные сокеты и непосредственно на канальном уровне.

Упражнения

1. Каково назначение флага

canjump
в листинге 29.7?

2. При работе программы

udpcksum
наиболее распространенным сообщением об ошибке является сообщение о недоступности порта ICMP (в пункте назначения не работает сервер имен) или недоступности узла ICMP. В обоих случаях нам не нужно ждать истечения времени ожидания, заданного функцией
udp_read
в листинге 29.6, так как сообщение о подобной ошибке фактически является ответом на наш запрос DNS. Модифицируйте программу таким образом, чтобы она перехватывала эти ошибки ICMP.

Глава 30

Альтернативное устройство клиента и сервера

30.1. Введение

При написании сервера под Unix мы можем выбирать из следующих вариантов управления процессом:

■ Наш первый сервер, показанный в листинге 1.5, был последовательным (iterative), но количество сценариев, для которых этот вариант является предпочтительным, весьма ограничено, поскольку последовательный сервер не может начать обработку очередного клиентского запроса, не закончив полностью обработку текущего запроса.

■ В листинге 5.1 показан первый в данной книге параллельный (concurrent) сервер, который для обработки каждого клиентского запроса порождал дочерний процесс с помощью функции

fork
. Традиционно большинство серверов, работающих под Unix, попадают в эту категорию.

■ В разделе 6.8 мы разработали другую версию сервера TCP, в котором имеется только один процесс, обрабатывающий любое количество клиентских запросов с помощью функции

select
.

■ В листинге 26.2 мы модифицировали параллельный сервер, создав для каждого клиента по одному потоку вместо одного процесса.

В этой главе мы рассмотрим два других способа модификации устройства параллельного сервера.

■ Предварительное создание дочерних процессов (preforking). В этом случае при запуске сервера выполняется функция

fork
, которая создает определенное количество (пул) дочерних процессов. Обработкой очередного клиентского запроса занимается процесс, взятый из этого набора.

■ Предварительное создание потоков (prethreading). При запуске сервера создается некоторое количество (пул) потоков, и для обработки каждого клиента используется поток из данного набора.

В данной главе мы будем рассматривать множество вопросов, связанных с предварительным созданием потоков и процессов. Например, что произойдет, если в пуле окажется недостаточное количество процессов или потоков? А если их будет слишком много? Как родительский и дочерние процессы (или потоки) синхронизируют свои действия?

Обычно написать клиент легче, чем сервер, за счет простоты управления процессом клиента. Тем не менее мы уже исследовали различные способы написания простого эхо-клиента, которые вкратце изложены в разделе 30.2.

В этой главе мы рассматриваем девять различных способов устройства сервера и взаимодействие каждого из этих серверов с одним и тем же клиентом. Клиент-серверный сценарий типичен для WWW: клиент посылает небольшой по объему запрос, а сервер отвечает ему, отсылая соответствующие запросу данные. Некоторые из этих серверов мы уже достаточно подробно обсуждали (например, параллельный сервер, вызывающий функцию

fork
для обработки каждого клиентского запроса), в то время как предварительное создание процессов и потоков являются новыми для нас концепциями, которые и будут подробно рассмотрены в этой главе.

Мы запускали различные экземпляры клиента с каждым сервером, измеряя время, которое процессор тратит на обслуживание определенного количества клиентских запросов. Чтобы информация об этом не оказалась рассеянной по всей главе, мы свели все полученные результаты в табл. 30.1, на которую в этой главе будем неоднократно ссылаться. Следует отметить, что значения времени, указанные в этой таблице, соответствуют процессорному времени, затраченному только на управление процессом, так как из фактического значения времени процессора мы вычитаем время, которое тратит на выполнение того же задания последовательный сервер, не имеющий накладных расходов, связанных с управлением процессом. Иными словами, нулевой точкой отсчета в данной таблице для нас является время, затраченное последовательным сервером. Для большей наглядности мы включили в таблицу строку для последовательного сервера с нулевыми значениями времени. В этой главе термином время центрального процессора на управление процессом (process control CPU time) мы обозначаем разность между фактическим значением времени центрального процессора и временем, затраченным последовательным сервером, для каждой конкретной системы.

Таблица 30.1. Сравнительные значения времени, затраченного каждым из обсуждаемых в данной главе сервером

Описание сервера Время центрального процессора на управление процессом
0 Последовательный (точка отсчета; затраты на управление процессом отсутствуют) 0,0
1 Параллельный сервер, один вызов функции fork для обработки одного клиента 20,90
2 Предварительное создание дочерних процессов, каждый из которых вызывает функцию accept 1,80
3 Предварительное создание дочерних процессов с блокировкой для защиты accept 2,07
4 Предварительное создание дочерних процессов с использованием взаимного исключения для защиты accept 1,75
5 Предварительное создание дочерних процессов, родительский процесс передает дочернему дескриптор сокета 2,58
6 Параллельный сервер, создание одного потока на каждый клиентский запрос 0,99
7 Предварительное создание потоков с использованием взаимного исключения для защиты accept 1,93
8 Предварительное создание потоков, главный поток вызывает accept 2,05

Все приведенные выше значения времени были получены путем запуска клиента, показанного в листинге 30.1, на двух различных узлах в той же подсети, что и сервер. Во всех тестах оба клиента порождали пять дочерних процессов для создания пяти одновременных соединений с сервером, таким образом максимальное количество одновременных соединений с сервером было равно 10. Каждый клиент запрашивал 4000 байт данных от сервера по каждому соединению. В случае, когда тест подразумевает предварительное создание дочерних процессов или потоков при запуске сервера, их количество равно 15.

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