UNIX: разработка сетевых приложений
Часть 186 из 399 Информация о книге
Инициализируются два набора дескрипторов, по одному для чтения и для записи.19-23— это максимальный дескриптор для функцииmaxfd(который мы инициализируем значением -1, поскольку дескрипторы неотрицательны),select— число файлов, которые осталось прочитать (когда это значение становится нулевым, чтение заканчивается),nlefttoread— это количество файлов, для которых пока еще требуется соединение TCP, anlefttoconn— это число соединений, открытых в настоящий момент (оно никогда не может превышать первый аргумент командной строки).nconnВ листинге 16.10 показана функция
, вызываемая один раз, когда начинается выполнение функции main.home_pageЛистинг 16.10. Функция home_page
//nonblock/home_page.c1 #include "web.h"2 void3 home_page(const char *host, const char *fname)4 {5 int fd, n;6 char line[MAXLINE];7 fd = Tcp_connect(host, SERV); /* блокируемая функция connect() */8 n = snprintf(line, sizeof(line), GET_CMD, fname);9 Writen(fd, line, n);10 for (;;) {11 if ((n = Read(fd, line, MAXLINE)) == 0)12 break; /* сервер закрыл соединение */13 printf("read %d bytes of home page\n", n);14 /* обрабатываем полученные данные */15 }16 printf("end-of-file on home page\n");17 Close(fd);18 }Установление соединения с серверомНаша функция7устанавливает соединение с сервером.tcp_connectОтправка команды HTTP серверу, чтение ответаЗапускается команда HTTP8-17для домашней страницы (часто обозначается символомGET). Читается ответ (с ответом мы в данном случае ничего не делаем), и соединение закрывается./Следующая функция,
, показанная в листинге 16.11, инициирует вызов неблокируемой функции connect.start_connectЛистинг 16.11. Инициирование неблокируемой функции connect
//nonblock/start_connect.c1 #include "web.h"2 void3 start_connect(struct file *fptr)4 {5 int fd, flags, n;6 struct addrinfo *ai;7 ai = Host_serv(fptr->f_host, SERV, 0, SOCK_STREAM);8 fd = Socket(ai->ai_family; ai->ai_socktype, ai->ai_protocol);9 fptr->f_fd = fd;10 printf("start_connect for %s, fd %d\n", fptr->f_name, fd);11 /* отключаем блокирование сокета */12 flags = Fcntl(fd, F_GETFL, 0);13 Fcntl(fd, F_SETFL, flags | O_NONBLOCK);14 /* инициируем неблокируемое соединение с сервером */15 if ((n = connected, ai->ai_addr, ai->ai_addrlen)) < 0) {16 if (errno != EINPROGRESS)17 err_sys("nonblocking connect error");18 fptr->f_flags = F_CONNECTING;19 FD_SET(fd, &rset); /* включаем дескриптор сокета в наборе чтенияи записи */20 FD_SET(fd, &wset);21 if (fd > maxfd)22 maxfd = fd;23 } else if (n >= 0) /* соединение уже установлено */24 write_get_cmd(fptr); /* отправляем команду GET серверу */25 }Создание сокета, отключение блокировки сокетаМы вызываем нашу функцию7-13для поиска и преобразования имени узла и имени службы. Она возвращает указатель на массив структурhost_serv. Мы используем только первую структуру. Создается сокет TCP, и он становится неблокируемым.addrinfoВызов неблокируемой функции connectВызывается неблокируемая функция14-22, и флагу файла присваивается значениеconnect. Включается дескриптор сокета и в наборе чтения, и в наборе записи, поскольку функцияF_CONNECTINGбудет ожидать любого из этих условий как указания на то, что установление соединения завершилось. При необходимости мы также обновляем значениеselect.maxfdОбработка завершения установления соединенияЕсли функция23-24успешно завершается, значит, соединение уже установлено, и функцияconnect(она показана в следующем листинге) посылает команду серверу.write_get_cmdМы делаем сокет неблокируемым для функции
, но никогда не переустанавливаем его в блокируемый режим, заданный по умолчанию. Это нормально, поскольку мы записываем в сокет только небольшое количество данных (команда GET следующей функции) и считаем, что эти данные занимают значительно меньше места, чем имеется в буфере отправки сокета. Даже если из-за установленного флага отсутствия блокировки при вызове функцииconnectпроисходит частичное копирование, наша функцияwriteобрабатывает эту ситуацию. Если оставить сокет неблокируемым, это не повлияет на последующее выполнение функцийwriten, потому что мы всегда вызываем функциюreadдля определения того момента, когда сокет станет готов для чтения.select