Второй способ является модификацией первого и применяется в приложениях, требующих мапой задержки(латентности) между моментом первого запроса и ответа сервера. Нужно только поменять fork & accept местами - создать заранее некоторый пул обслуживающих процессов, каждый из которых до прихода клиентского запроса будет заблокирован на accept (accept на одном и том же прослушиваемом сокете). А после отработки клиентского запроса заблаговременно создать новый обслуживающий процесс. Эта техника известна как «предварительный fork» или pre-fork.
Фрагмент кода программы сервера с использованием этой методики показан в листинге 2. Для более четкого показа приводится код программы на C++.
Листинг 2. Код сервер (версия pre- fork)
#include <common.h>
Const int NUMPROC = 3;
int main(int argc, char *argv[])
{
int ls = getsocket(PREFORK_PORT), rs;
for(int i = 0; i < NUMPROC; i++) {
if(fork() == 0) {
int rs;
while(true) {
if((rs = accept(ls, NULL, NULL)) < 0) errx("accept error");
server(rs); /* В подпрограмме server реализуется логика работы сервера */
close(rs);
cout << i << flush;
delay(250);
};
};
};
for(int i = 0; i < NUMPROC; i++) waitpid(0, NULL, 0);
exit(EXIT_SUCCESS);
}
При написании этого текста я несколько «схитрил» и упростил в сравнении с предложенной абзацем выше моделью. Здесь 3 обслуживающих процесса сделаны циклическими и не завершаются по окончанию обслуживания, а снова блокируются на accept, но для наблюдения эффектов этого вполне достаточно (последняя строка нужна вообще только для блокировки родительского процесса, и «сохранения» управляющего терминала - для возможности прекращения всей группы процессов по ^C или по kill -TERM... в случае, когда сервер запущен в фоновом режиме).