26.6. Веб-клиент и одновременное соединение (продолжение)
26.6. Веб-клиент и одновременное соединение (продолжение)
Вернемся к нашему примеру с веб-клиентом из раздела 16.5 и перепишем его с использованием потоков вместо неблокируемой функции connect. Мы можем оставить сокеты в их заданном по умолчанию виде — блокируемыми, и создать один поток на каждое соединение. Каждый поток может блокироваться в вызове функции connect, так как ядро будет просто выполнять какой-либо другой поток, готовый к работе.
В листинге 26.7 показана первая часть нашей программы, глобальные переменные и начало функции main.
Листинг 26.7. Глобальные переменные и начало функции main
//threads/web01.c
1 #include "unpthread.h"
2 #include <thread.h> /* потоки Solaris */
3 #define MAXFILES 20
4 #define SERV "80" /* номер порта или имя службы */
5 struct file {
6 char *f_name; /* имя файла */
7 char *f_host; /* имя узла или IP-адрес */
8 int f_fd; /* дескриптор */
9 int f_flags; /* F_xxx ниже */
10 pthread_t f_tid; /* идентификатор потока */
11 } file[MAXFILES];
12 #define F_CONNECTING 1 /* функция connect () в процессе
выполнения */
13 #define F_READING 2 /* функция connect() завершена;
выполняется считывание */
14 #define F_DONE 4 /* все сделано */
15 #define GET_CMD "GET %s HTTP/1.0 "
16 int nconn, nfiles, nlefttoconn, nlefttoread;
17 void *do_get_read(void*);
18 void home_page(const char*, const char*);
19 void write_get_cmd(struct file*);
20 int
21 main(int argc, char **argv)
22 {
23 int i, n, maxnconn;
24 pthread_t tid;
25 struct file *fptr;
26 if (argc < 5)
27 err_quit("usage: web <#conns> <IPaddr> <homepage> file1 ...");
28 maxnconn = atoi(argv[1]);
29 nfiles = min(argc - 4, MAXFILES);
30 for (i = 0; i < nfiles; i++) {
31 file[i].f_name = argv[i + 4];
32 file[i].f_host = argv[2];
33 file[i].f_flags = 0;
34 }
35 printf("nfiles = %d ", nfiles);
36 home_page(argv[2], argv[3]);
37 nlefttoread = nlefttoconn = nfiles;
38 nconn = 0;
Глобальные переменные
1-16 Мы подключаем заголовочный файл <thread.h> вдобавок к обычному <pthread.h>, так как нам требуется использовать потоки Solaris в дополнение к потокам Pthreads, как мы вскоре покажем.
10 Мы добавили к структуре file один элемент — идентификатор потока f_tid. Остальная часть этого кода аналогична коду в листинге 16.9. В этой версии нам не нужно использовать функцию select, а следовательно, не нужны наборы дескрипторов и переменная maxfd.
36 Функция home_page не изменилась относительно листинга 16.10. В листинге 26.8 показан основной рабочий цикл потока main.
Листинг 26.8. Основной рабочий цикл потока main
//threads/web01.c
39 while (nlefttoread > 0) {
40 while (nconn < maxnconn && nlefttoconn > 0) {
41 /* находим файл для считывания */
42 for (i = 0; i < nfiles; i++)
43 if (file[i].f_flags == 0)
44 break;
45 if (i == nfiles)
46 err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
47 file[i].f_flags = F_CONNECTING;
48 Pthread_create(&tid, NULL, &do_get_read, &file[i]);
49 file[i].f_tid = tid;
50 nconn++;
51 nlefttoconn--;
52 }
53 if ((n = thr_join(0, &tid, (void**)&fptr)) != 0)
54 errno = n, err_sys("thr_join error");
55 nconn--;
56 nlefttoread--;
57 printf("thread id %d for %s done ", tid, fptr->f_name);
58 }
59 exit(0);
60 }
По возможности создаем другой поток
40-52 Если имеется возможность создать другой поток (nconn меньше, чем maxconn), мы так и делаем. Функция, которую выполняет каждый новый поток, — это do_get_read, а ее аргументом является указатель на структуру file.
Ждем, когда завершится выполнение какого-либо потока
53-54 Мы вызываем функцию потоков thr_join Solaris с нулевым первым аргументом, чтобы дождаться завершения выполнения какого-либо из наших потоков. К сожалению, в Pthreads не предусмотрен способ, с помощью которого мы могли бы ждать завершения выполнения любого потока, и функция pthread_join требует, чтобы мы точно указали, завершения какого потока мы ждем. В разделе 26.9 мы увидим, что решение этой проблемы в случае применения технологии Pthreads оказывается сложнее и требует использования условной переменной для сообщения главному потоку о завершении выполнения дополнительного потока.
ПРИМЕЧАНИЕ
Показанное здесь решение, в котором используется функция потоков thr_join Solaris, не является, вообще говоря, совместимым со всеми системами. Тем не менее мы приводим здесь эту версию веб-клиента, использующую потоки, чтобы не осложнять обсуждение рассмотрением условных переменных и взаимных исключений (mutex). К счастью, в Solaris допустимо смешивать потоки Pthreads и потоки Solaris.
В листинге 26.9 показана функция do_get_read, которая выполняется каждым потоком. Эта функция устанавливает соединение TCP, посылает серверу команду HTTP GET и считывает ответ сервера.
Листинг 26.9. Функция do_get_read
//threads/web01.c
61 void*
62 do_get_read(void *vptr)
63 {
64 int fd, n;
65 char line[MAXLINE];
66 struct file *fptr;
67 fptr = (struct file*)vptr;
68 fd = Tcp_connect(fptr->f_host, SERV);
69 fptr->f_fd = fd;
70 printf("do_get_read for %s, fd %d, thread %d ",
71 fptr->f_name, fd, fptr->f_tid);
72 write_get_cmd(fptr);
73 /* Чтение ответа сервера */
74 for (;;) {
75 if ((n = Read(fd, line, MAXLINE)) == 0)
76 break; /* сервер закрывает соединение */
77 printf ("read %d bytes from %s ", n, fptr->f_name);
78 }
79 printf("end-of-file on %s ", fptr->f_name);
80 Close(fd);
81 fptr->f_flags = F_DONE; /* сбрасываем F_READING */
82 return (fptr); /* завершение потока */
83 }
Создание сокета TCP, установление соединения
68-71 Создается сокет TCP, и с помощью функции tcp_connect устанавливается соединение. В данном случае используется обычный блокируемый сокет, поэтому поток будет блокирован при вызове функции connect, пока не будет установлено соединение.
Отправка запроса серверу
72 Функция write_get_cmd формирует команду HTTP GET и отсылает ее серверу. Мы не показываем эту функцию заново, так как единственным отличием от листинга 16.12 является то, что в версии, использующей потоки, не вызывается макрос FD_SET и не используется maxfd.
Чтение ответа сервера
73-82 Затем считывается ответ сервера. Когда соединение закрывается сервером, устанавливается флаг F_DONE и функция возвращает управление, завершая выполнение потока.
Мы также не показываем функцию home_page, так как она полностью повторяет версию, приведенную в листинге 16.10.
Мы вернемся к этому примеру, заменив функцию Solaris thr_join на более переносимую функцию семейства Pthreads, но сначала нам необходимо обсудить взаимные исключения и условные переменные.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКДанный текст является ознакомительным фрагментом.
Читайте также
17.4.3. Соединение с сервером
17.4.3. Соединение с сервером Процесс соединения с сервером через сокет домена Unix состоит из создания сокета и присоединения к требуемому адресу через функцию connect(). Как только сокет присоединен, он может обрабатываться как любой другой файловый дескриптор.Следующая
Разовое соединение
Разовое соединение Совершенно не обязательно вносить новое соединение в свою записную книжку, если вам надо подключиться к удаленному компьютеру только один раз. Для этого используется разовое соединение, которое доступно через меню Соединение | Соединение с (рис. 8.9).
2.1. Модемное соединение
2.1. Модемное соединение 2.1.1. Выбор модема Модем модему рознь. Модемы различаются по разным признакам, но главные из них - это способ подключения к компьютеру и организация взаимодействия с ним.Модем может подключаться или к СОМ-порту или к шине USB. Раньше у Linux были проблемы
2.2. ADSL-соединение
2.2. ADSL-соединение 2.2.1. Преимущества ADSL-соединения В последнее время очень популярны ADSL-соединения (Asymmetric Digital Subscriber Line, асимметричная цифровая абонентская линия), Причина их популярности проста: высокая скорость и дешевизна, Как правило, в месяц безлимитное ADSL-соединение
6.3. Одновременное изображение диаграмм в отдельных системах координат
6.3. Одновременное изображение диаграмм в отдельных системах координат На рис. 6.8 в одной системе координат изображены четыре диаграммы. Этот метод подходит для двух или даже трех диаграмм, однако, если требуется большее количество, то ориентироваться в них становится
Одновременное форматирование нескольких элементов управления
Одновременное форматирование нескольких элементов управления Многие команды меню Format предназначены для работы сразу с несколькими элементами управления или с несколькими группами элементов управления. Все эти команды будут обсуждаться в данном разделе, но сначала
Внутреннее соединение
Внутреннее соединение Следующий оператор соединяет две таблицы, которые связаны через внешний ключ FK правой таблицы (Table2) и первичный ключ PK таблицы Table1:SELECTТаblе1.PK,Table1.COL1,Table2.PKX,Table2.COLXFROM Table1 INNER JOIN Table2ON Table1.PK = Table2.FKWHERE... условия-поискаЭто спецификация внутреннего
Инфракрасное соединение
Инфракрасное соединение Несмотря на растущую популярность Wi-Fi, Bluetooth и других беспроводных технологий, по-прежнему не сдает своих позиций и передача данных через инфракрасный порт. Например, все мы каждый день применяем инфракрасный порт при использовании
Пример 16-3. Одновременное перенаправление устройств, stdin и stdout, с помощью команды exec
Пример 16-3. Одновременное перенаправление устройств, stdin и stdout, с помощью команды exec #!/bin/bash# upperconv.sh# Преобразование символов во входном файле в верхний регистр.E_FILE_ACCESS=70E_WRONG_ARGS=71if [ ! -r "$1" ] # Файл доступен для чтения?then echo "Невозможно прочитать из заданного файла!" echo
Как разорвать соединение?
Как разорвать соединение? Соединение с Интернетом через мобильный телефон — коммутируемое. Другими словами, каждый раз, когда вам понадобится выход во Всемирную сеть, его следует включать, а когда доступ уже не требуется — выключать (разрывать). Отключиться от Интернета
4.8. Этап 3: нажатие кнопки затвора и одновременное срабатывание системы автоматической фокусировки
4.8. Этап 3: нажатие кнопки затвора и одновременное срабатывание системы автоматической фокусировки Режимы протяжки Цифровым камерам (правда, не всем) доступно несколько режимов съемки, аналогичных тем, которые в традиционной фотографии называются режимами протяжки
6.1. Модемное соединение
6.1. Модемное соединение К Интернету можно подключиться разными способами. Один из наиболее часто используемых — удаленное соединение по коммутируемым сетям общего пользования через модем, выполняющий модуляцию и демодуляцию (отсюда и название) дискретных сигналов.
6.3. Оптимальное: DSL-соединение
6.3. Оптимальное: DSL-соединение DSL (Digital Subscriber Line) — цифровая абонентская линия, позволяющая производить двунаправленный обмен данными по телефонной линии. Существует несколько вариантов DSL-линий: ADSL, VDSL, SDSL, RADSL. Наиболее распространены ADSL-линии. ADSL (Asymmetric DSL) —
Соединение с партнерами
Соединение с партнерами Компания JFC Farmaceutical захотела предоставить доступ к своей информации одному из своих клиентов для ускорения совместного исследовательского проекта. Клиенту, компании McConnell Drugs, был нужен доступ к информации, хранящейся на сервере базы данных (Drug10).
Стивенс Уильям Ричард
Просмотр ограничен
Смотрите доступные для ознакомления главы 👉