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

Функция

child_main
показана в листинге 30.19.

Листинг 30.19. Функция child_main: передача дескриптора в сервере с предварительным порождением дочерних процессов

//server/child05.c

23 void

24 child_main(int i, int listenfd, int addrlen)

25 {

26  char c;

27  int connfd;

28  ssize_t n;

29  void web_child(int);

30  printf("child %ld starting\n", (long)getpid());

31  for (;;) {

32   if ((n = Read_fd(STDERR_FILENO, &c, 1, &connfd)) == 0)

33    err_quit("read_fd returned 0");

34   if (connfd < 0)

35    err_quit("no descriptor from read_fd");

36   web_child(connfd); /* обработка запроса */

37   Close(connfd);

38   Write(STDERR_FILENO, "", 1); /* сообщаем родительскому процессу

                                     о том, что дочерний освободился */

39  }

40 }

Ожидание дескриптора от родительского процесса

32-33
 Эта функция отличается от аналогичных функций из двух предыдущих разделов, так как дочерний процесс не вызывает более функцию
accept
. Вместо этого дочерний процесс блокируется в вызове функции
read_fd
, ожидая, когда родительский процесс передаст ему дескриптор присоединенного сокета.

Сообщение родительскому процессу о готовности дочернего к приему новых запросов

38
 Закончив обработку очередного клиентского запроса, мы записываем (
write
) 1 байт в канал, чтобы сообщить, что данный дочерний процесс освободился.

В табл. 30.1 при сравнении строк 4 и 5 мы видим, что данный сервер медленнее, чем версия, рассмотренная нами в предыдущем разделе, которая использовала блокировку потоками взаимного исключения. Передача дескриптора по каналу от родительского процесса к дочернему и запись одного байта в канал для сообщения родительскому процессу о завершении обработки клиентского запроса занимает больше времени, чем блокирование и разблокирование взаимного исключения или файла.

В табл. 30.2 показаны значения счетчиков

child_count
из структуры
Child
, которые выводятся обработчиком сигнала
SIGINT
по завершении работы сервера. Дочерние процессы, расположенные ближе к началу массива, обрабатывают большее количество клиентских запросов, как было указано при обсуждении листинга 30.18.

30.10. Параллельный сервер TCP: один поток для каждого клиента

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

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

Наша первая версия сервера с использованием потоков показана в листинге 30.20. Это модификация листинга 30.2: в ней создается один поток для каждого клиента вместо одного дочернего процесса для каждого клиента. Эта версия во многом похожа на сервер, представленный в листинге 26.2.

Листинг 30.20. Функция main для сервера TCP, использующего потоки

//server/serv06.c

 1 #include "unpthread.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int listenfd, connfd;

 6  void sig_int(int);

 7  void *doit(void*);

 8  pthread_t tid;

 9  socklen_t clilen, addrlen;

10  struct sockaddr *cliaddr;

11  if (argc == 2)

12   listenfd = Tcp_listen(NULL, argv[1], &addrlen);

13  else if (argc == 3)

14   listenfd = Tcp_listen(argv[1], argv[2], &addrlen);

15  else

16   err_quit("usage: serv06 [ <host> ] <port#>");

17  cliaddr = Malloc(addrlen);

18  Signal (SIGINT, sig_int);

19  for (;;) {

20   clilen = addrlen;

21   connfd = Accept(listenfd, cliaddr, &clilen);

22   Pthread_create(&tid, NULL, &doit, (void*)connfd);

23  }

24 }

25 void*

26 doit(void *arg)

27 {

28  void web_child(int);

29  Pthread_detach(pthread_self());

30  web_child((int)arg);

31  Close((int)arg);

32  return (NULL);

33 }

Цикл основного потока

19-23
 Основной поток блокируется в вызове функции accept, и каждый раз, когда прибывает новое клиентское соединение, функцией
pthread_create
создается новый поток. Функция, выполняемая новым потоком, — это функция
doit
, а ее аргументом является присоединенный сокет.

Функция прочих потоков

25-33
 Функция
doit
выполняется как отсоединенный (detached) поток, потому что основному потоку не требуется ждать ее завершения.
Doit
вызывает функцию
web_child
(см. листинг 30.5). Когда эта функция возвращает управление, присоединенный сокет закрывается.

354
{"b":"225366","o":1}
ЛитМир: бестселлеры месяца