12
tid
. Функция, выполняемая новым потоком, — это copyto
. Никакие аргументы потоку не передаются.13-14
readline
и fputs
, которые осуществляют копирование из сокета в стандартный поток вывода.15
str_cli
возвращает управление, функция main завершается при помощи вызова функции exit
(см. раздел 5.4). При этом завершаются все потоки данного процесса. В обычном сценарии второй поток уже должен завершиться в результате считывания признака конца файла из стандартного потока ввода. Но в случае, когда сервер преждевременно завершил свою работу (см. раздел 5.12), при вызове функции exit
завершается также и второй поток, чего мы и добиваемся.16-25
shutdown
и отсылается сегмент FIN, после чего поток возвращает управление. При выполнении оператора return
(то есть когда функция, запустившая поток, возвращает управление) поток также завершается.В конце раздела 16.2 мы привели результаты измерений времени выполнения для пяти различных реализаций функции str_cli
fork
(как мы и ожидали), но медленнее, чем версия с неблокируемым вводом-выводом. Тем не менее, сравнивая устройство версии с неблокируемым вводом-выводом (см. раздел 16.2) и версии с использованием потоков, мы заметили, что первая гораздо сложнее. Поэтому мы рекомендуем использовать именно версию с потоками, а не с неблокируемым вводом-выводом.26.4. Использование потоков в эхо-сервере TCP
Теперь мы перепишем эхо-сервер TCP, приведенный в листинге 5.1, используя для каждого клиента по одному потоку вместо одного процесса. Кроме того, с помощью нашей функции tcp_listen
Листинг 26.2
. Эхо-сервер TCP, использующий потоки//threads/tcpserv01.с
1 #include "unpthread.h"
2 static void *doit(void*); /* каждый поток выполняет эту функцию */
3 int
4 main(int argc, char **argv)
5 {
6 int listenfd, connfd;
7 pthread_t tid;
8 socklen_t addrlen, len;
9 struct sockaddr *cliaddr;
10 if (argc == 2)
11 listenfd = Tcp_listen(NULL, argv[1], &addrlen);
12 else if (argc == 3)
13 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
14 else
15 err_quit("usage: tcpserv01 [
16 cliaddr = Malloc(addrlen);
17 for (;;) {
18 len = addrlen;
19 connfd = Accept(listenfd, cliaddr, &len);
20 Pthread_create(&tid, NULL, &doit, (void*)connfd);
21 }
22 }
23 static void*
24 doit(void *arg)
25 {
26 Pthread_detach(pthread_self());
27 str_echo((int)arg); /* та же функция, что и раньше */
28 Close((int)arg); /* мы закончили с присоединенным сокетом */
29 return (NULL);
30 }
17-21
accept
возвращает управление, мы вызываем функцию pthread_create
вместо функции fork
. Мы передаем функции doit
единственный аргумент — дескриптор присоединенного сокета connfd
.