UNIX: разработка сетевых приложений
Шрифт:
Мы запускали различные экземпляры клиента с каждым сервером, измеряя время, которое процессор тратит на обслуживание определенного количества клиентских запросов. Чтобы информация об этом не оказалась рассеянной по всей главе, мы свели все полученные результаты в табл. 30.1, на которую в этой главе будем неоднократно ссылаться. Следует отметить, что значения времени, указанные в этой таблице, соответствуют процессорному времени, затраченному только на управление процессом, так как из фактического значения времени процессора мы вычитаем время, которое тратит на выполнение того же задания последовательный сервер, не имеющий накладных расходов, связанных с управлением процессом. Иными словами, нулевой точкой отсчета в данной таблице для нас является время, затраченное последовательным сервером. Для большей наглядности мы включили в таблицу строку для последовательного сервера с нулевыми значениями времени. В этой главе термином время
Таблица 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.
Некоторые версии нашего сервера работали с предварительно созданным пулом потоков или процессов. Интересным моментом является распределение клиентских запросов по потокам или дочерним процессам, находящимся в накопителе. В табл. 30.2 показаны варианты этого распределения, которые также будут обсуждаться в соответствующих разделах.
Таблица 30.2. Количество клиентов, обслуженных каждым из 15 дочерних процессов или потоков
№ процесса или потока | Предварительное создание процессов без защиты accept (строка 2) | Предварительное создание процессов с защитой accept (строка 3) | Предварительное создание процессов, передача дескриптора (строка 5) | Предварительное порождение потоков, защита accept (строка 7) |
---|---|---|---|---|
0 | 333 | 347 | 1006 | 333 |
1 | 340 | 328 | 950 | 323 |
2 | 335 | 332 | 720 | 333 |
3 | 335 | 335 | 583 | 328 |
4 | 332 | 338 | 485 | 329 |
5 | 331 | 340 | 457 | 322 |
6 | 333 | 335 | 385 | 324 |
7 | 333 | 343 | 250 | 360 |
8 | 332 | 324 | 105 | 341 |
9 | 331 | 315 | 32 | 348 |
10 | 334 | 326 | 14 | 358 |
11 | 333 | 340 | 9 | 331 |
12 | 334 | 330 | 4 | 321 |
13 | 332 | 331 | 1 | 329 |
14 | 332 | 336 | 0 | 320 |
5000 | 5000 | 5000 | 5000 |
30.2. Альтернативы для клиента TCP
Мы уже обсуждали различные способы устройства клиентов, но стоит тем не менее еще раз обратить внимание на относительные достоинства и недостатки этих способов.
1. В листинге 5.4 показан основной способ устройства клиента TCP. С этой программой были связаны две проблемы. Во-первых, когда она блокируется в ожидании ввода пользователя, она не замечает происходящих в сети событий, например отключения собеседника от соединения. Во-вторых, она действует в режиме остановки и ожидания, что неэффективно в случае пакетной обработки.
2. Листинг 6.1 содержит следующую, модифицированную версию клиента. С помощью функции
3. С листинга 16.1 начинается рассмотрение клиентов, использующих неблокируемый ввод-вывод.
4. Первым из рассмотренных нами клиентов, вышедшим за пределы ограничений, связанных с наличием единственного процесса или потока для обслуживания всех запросов, является клиент, изображенный в листинге 16.6. В этом случае использовалась функция
5. В листинге 26.1 используются два потока вместо двух процессов.
В конце раздела 16.2 мы резюмируем различия между перечисленными версиями. Как мы отметили, хотя версия с неблокируемым вводом-выводом является самой быстродействующей, ее код слишком сложен, а применение двух потоков или двух процессов упрощает код.
30.3. Тестовый клиент TCP
В листинге 30.1 [1] показан клиент, который будет использоваться для тестирования всех вариаций нашего сервера.
Листинг 30.1. Код клиента TCP для проверки различных версий сервера
1
Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.