В третьем сценарии мы выполняем три параллельных соединения и снова считаем, что эти три соединения не мешают друг другу (идеальный случай). Но общее время при этом такое же (15 единиц), как и во втором сценарии.
При работе с веб-клиентами первое соединение устанавливается само по себе, за ним следуют соединения по ссылкам, обнаруженным в данных от первого соединения. Мы показываем это на рис. 16.6.
Рис. 16.6. Установление первого соединения, а затем множества
параллельных соединений
Для дальнейшей оптимизации клиент может начать обработку данных, возвращаемых по первому соединению, до того, как установление первого соединения завершится, и инициировать дополнительные соединения, как только ему станет известно, что они нужны.
Поскольку мы выполняем несколько неблокируемых функций
connect
одновременно, мы не можем использовать нашу функцию
connect_nonb
, показанную в листинге 16.7, так как она не завершается, пока соединение не установлено. Вместо этого мы отслеживаем множество соединений самостоятельно.
Наша программа считывает около 20 строк с веб-сервера. Мы задаем в качестве аргументов командной строки максимальное число параллельных соединений, имя узла сервера, а затем каждое из имен файлов, получаемых с сервера. Типичное выполнение нашей программы выглядит так:
solaris % web % www.foobar.com / image1.gif image2.gif \
image3.gif image4.gif image5.gif \
image6.gif image7.gif
Аргументы командной строки задают три одновременных соединения, имя узла сервера, имя файла домашней страницы (
/
обозначает корневой каталог сервера) и семь файлов, которые затем нужно прочитать (в нашем примере это файлы с изображениями в формате GIF). Обычно на эти семь файлов имеются ссылки с домашней страницы, и чтобы получить их имена, веб-клиент читает домашнюю страницу и обрабатывает код HTML. Чтобы не усложнять этот пример разбором кода HTML, мы просто задаем имена файлов в командной строке.
Это большой пример, поэтому мы будем показывать его частями. В листинге 16.8 представлен наш заголовочный файл
web.h
, который включен во все файлы.
Листинг 16.8. Заголовок web.h
//nonblock/web.h
1 #include "unp.h"
2 #define MAXFILES 20
3 #define SERV "80" /* номер порта или имя службы */
4 struct file {
5 char *f_name; /* имя файла */
6 char *f_host; /* имя узла или адрес IPv4/IPv6 */
7 int f_fd; /* дескриптор */
8 int f_flags; /* F_xxx определены ниже */
9 } file[MAXFILES];
10 #define F_CONNECTING 1 /* connect в процессе выполнения */
11 #define F_READING 2 /* соединение установлено; происходит считывание */
12 #define F_DONE 4 /*
все сделано */
13 #define GET_CMD "GET %s HTTP/1.0\r\n\r\n"
14 /* глобальные переменные */
15 int nconn, nfiles, nlefttoconn, nlefttoread, maxfd;
16 fd_set rset, wset;
17 /* прототипы функций */
18 void home_page(const char*, const char*);
19 void start_connect (struct file*);
20 void write_get_cmd(struct file*);
Задание структуры file
2-13
Программа считывает некоторое количество (не более
MAXFILES
) файлов с веб-сервера. Структура
file
содержит информацию о каждом файле: его имя (копируется из аргумента командной строки), имя узла или IP-адрес сервера, с которого читается файл, дескриптор сокета, используемый для этого файла, и набор флагов, которые указывают, что мы делаем с этим файлом (устанавливаем соединение для получения файла или считываем файл).
Определение глобальных переменных и прототипов функций
14-20
Мы определяем глобальные переменные и прототипы для наших функций, которые мы вскоре опишем.
Листинг 16.9. Первая часть программы одновременного выполнения функций connect: глобальные переменные и начало функции main
//nonblock/web.c
1 #include "web.h"
2 int
3 main(int argc, char **argv)
4 {
5 int i, fd, n, maxnconn, flags, error;
6 char buf[MAXLINE];
7 fd_set rs, ws;
8 if (argc < 5)
9 err_quit("usage: web <#conns> <hostname> <homepage> <file1> ...");