Книга: UNIX: разработка сетевых приложений
30.10. Параллельный сервер TCP: один поток для каждого клиента
Разделы на этой странице:
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). Когда эта функция возвращает управление, присоединенный сокет закрывается.
Из табл. 30.1 мы видим, что эта простая версия с использованием потоков является более быстродействующей, чем даже самая быстрая из версий с предварительным порождением процессов. Кроме того, эта версия, в которой каждый клиент обслуживается одним потоком, во много раз быстрее версии, в которой каждый клиент обслуживается специально созданным для него дочерним процессом (первая строка табл. 30.1).
ПРИМЕЧАНИЕ
В разделе 26.5 мы упомянули о трех вариантах преобразования функции, которая не является безопасной в многопоточной среде, в функцию, обеспечивающую требуемую безопасность. Функция web_child вызывает функцию readline, и версия, показанная в листинге 3.12, не является безопасной в многопоточной среде. На примере, приведенном в листинге 30.20, были испробованы вторая и третья альтернативы из раздела 26.5. Увеличение быстродействия при переходе от альтернативы 3 к альтернативе 2 составило менее одного процента, вероятно, потому, что функция readline использовалась лишь для считывания значения счетчика (5 символов) от клиента. Поэтому в данной главе для простоты мы использовали более медленную версию из листинга 3.11 для сервера с предварительным порождением потоков.
- 30.1. Введение
- 30.2. Альтернативы для клиента TCP
- 30.3. Тестовый клиент TCP
- 30.4. Последовательный сервер TCP
- 30.5. Параллельный сервер TCP: один дочерний процесс для каждого клиента
- 30.6. Сервер TCP с предварительным порождением процессов без блокировки для вызова accept
- 30.7. Сервер TCP с предварительным порождением процессов и защитой вызова accept блокировкой файла
- 30.8. Сервер TCP с предварительным порождением процессов и защитой вызова accept при помощи взаимного исключения
- 30.9. Сервер TCP с предварительным порождением процессов: передача дескриптора
- 30.10. Параллельный сервер TCP: один поток для каждого клиента
- 30.11. Сервер TCP с предварительным порождением потоков, каждый из которых вызывает accept
- 30.12. Сервер с предварительным порождением потоков: основной поток вызывает функцию accept
- 30.13. Резюме
- Упражнения
- 30.2. Альтернативы для клиента TCP
- Запуск InterBase-сервера
- Расширенная установка InterBase-сервера
- Совместимость клиентов и серверов различных версий
- Достоинства и недостатки потоков
- Статистика InterBase-сервера
- Сервер для InterBase
- Аватар идеального клиента
- 1.3.3. Достоинства и недостатки анонимных прокси-серверов
- Минимальный состав сервера InterBase SuperServer
- Отличительные особенности сервера Yaffil
- Встраиваемый сервер